一、Go语言中同步的两种方式
同步是一种计算机术语一般指的是在两个或多个数据库、文件、模块、线程之间用来保持数据内容一致性的机制。
并发编程必然要讨论同步,多线程操作同一资源时,如果不能协调好其同步进程,就会出现死锁或数据不一致的问题。举个简单的例子,两个线程同时对变量v(初始值为0)进行加1操作,我们希望两次执行后结果为2,但在并发环境下结果很可能是1。
对于竞争资源的访问就需要同步来协调啦。在Go语言中,有两种同步方式,其一是sync包提供同步操作,调用sync.WaitGroup阻塞所有goroutine直到goroutine执行完毕后才会进入下一步操作,这样就可以让并发操作串行执行。其二是利用channel,各个goroutine共同读写竞争资源转换为共同接收或发送channel,又因为channel自身就可以实现阻塞,从而简化了同步代码。
二、自增ID Server的实现
Don't BB, Show me code. 光说不练假把式,下面我们利用channel实现一个自增ID Server。在各类业务场景中,我们都会遇到为不同的请求分配自增ID,保证各个并发请求获得的ID都是唯一不重复的。这个其实就是多并发访问一个共享变量取值就是的过程,在任一时刻,只允许一个goroutine访问该变量,因此只需要一个容量为1的channel即可解决。
var ch = make(chan struct{}, 1)
func send_channel(){
ch<- struct {}{}
}
func increateIDWithFile(configTypePrefix string) (id string, err error){
<- ch
defer send_channel()
b, err := ioutil.ReadFile(configTypePrefix)
if err != nil {
return "", errors.New("ConfigTypePrefix FIle Read Error")
}
currentCountStr := string(b)
currentCount, err := strconv.ParseInt(currentCountStr, 10, 64)
if err != nil {
return "", errors.New("ConfigType Current Value is illegal")
}
currentCount ++
currentCountStr = strconv.FormatInt(currentCount, 10)
err = ioutil.WriteFile(configTypePrefix, []byte(currentCountStr), 0644)
if err != nil {
return "", errors.New("ConfigTypePrefix File Write Error")
}
configTypePrefix = configTypePrefix+"000000000"
splitLen := len(configTypePrefix) - len(currentCountStr)
return configTypePrefix[:splitLen]+currentCountStr, nil
}
猜一猜,为什么要把变量写在文件中呢?原因很简单,server关停后后内存中的变量会被清除,为了服务重启后请求获得的ID是正确的,必须数据持久化。
本文分享自微信公众号 - DevOps技术说(sli4book)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/u/4621641/blog/4518434