内容简介:我在接手其他同事的 golang 项目时,一般都会习惯性的做一个竞态检测。有时总会得到一些“惊喜”,比如像下面这段代码:当通过我觉得不同的 goroutine 并发读写同一个变量,需要加锁,这应该是天经地义的常识。
Any race is a bug
我在接手其他同事的 golang 项目时,一般都会习惯性的做一个竞态检测。有时总会得到一些“惊喜”,比如像下面这段代码:
package main import ( "fmt" "runtime" "time" ) var i = 0 func main() { runtime.GOMAXPROCS(2) go func() { for { fmt.Println("i is", i) time.Sleep(time.Second) } }() for { i += 1 } }
当通过 go run -race cmd.go
执行时,可以看到有明显的竞态出现:
================== WARNING: DATA RACE Read at 0x0000005e4600 by goroutine 6: main.main.func1() /root/gofourge/src/lab/cmd.go:15 +0x63 Previous write at 0x0000005e4600 by main goroutine: main.main() /root/gofourge/src/lab/cmd.go:20 +0x7b Goroutine 6 (running) created at: main.main() /root/gofourge/src/lab/cmd.go:13 +0x4f ================== i is: 8212 i is: 54959831 i is: 109202117
我觉得不同的 goroutine 并发读写同一个变量,需要加锁,这应该是天经地义的常识。 但是总有人以为,不加锁导致的问题最多就是读取的数据是修改前的数据,不能保证原子性罢了。是这样的吗?从上面的输出来看,似乎也差不多,其实这些都是典型的误解。
有些朋友可能不知道,在 Go(甚至是大部分语言)中,一条普通的赋值语句其实并不是一个原子操作(语言规范同样没有定义 `i++` 是原子操作, 任何变量的赋值都不是原子操作) 。例如,在 32 位机器上写 int64
类型的变量是有中间状态的,它会被拆成两次写操作 MOV
—— 写低 32 位和写高 32 位,如下图所示:
如果一个线程刚写完低 32 位,还没来得及写高 32 位时,另一个线程读取了这个变量,那它得到的就是一个毫无逻辑的中间变量,这很有可能使我们的程序出现诡异的 Bug。
而在 Go 的内存模型中,有 race 的 Go 程序的行为是未定义行为,理论上出现什么情况都是正常的。就拿上面的代码来说,当去掉 -race
参数执行时,大概率会得到这样的输出:
i is: 0 i is: 0 i is: 0 i is: 0
而用较老的 go 版本执行时,基本上执行一段时间,程序就会 HANG 住。所以讨论为什么出现这种现象实际上没有任何意义,不要依赖这种行为。
Mutex vs Atomic
解决 race 的问题时,无非就是上锁。可能很多人都听说过一个高逼格的词叫「无锁队列」。 都一听到加锁就觉得很 low,那无锁又是怎么一回事?其实就是利用 atomic
特性,那 atomic
会比 mutex
有什么好处呢? Benign Data Races: What Could Possibly Go Wrong? 的作者总结了这两者的一个区别:
Mutexes do no scale. Atomic loads do.
mutex
由 操作系统 实现,而 atomic
包中的原子操作则由 底层硬件 直接提供支持。在 CPU 实现的指令集里,有一些指令被封装进了 atomic
包,这些指令在执行的过程中是不允许中断(interrupt)的,因此原子操作可以在 lock-free 的情况下保证并发安全,并且它的性能也能做到随 CPU 个数的增多而线性扩展。
若实现相同的功能,后者通常会更有效率,并且更能利用计算机多核的优势。所以,以后当我们想并发安全的更新一些变量的时候,我们应该优先选择用 atomic
来实现。
参考资料
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
大型网站技术架构
李智慧 / 电子工业出版社 / 2013-9-1 / 59.00元
《大型网站技术架构:核心原理与案例分析》通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计案例,为读者呈现一幅包括技术选型、架构设计、性能优化、Web 安全、系统发布、运维监控等在内的大型网站开发全景视图。 《大型网站技术架构:核心原理与案例分析》不仅适用于指导网站工程师、架构师进行网站技术架构设计,也可用于指导产品经......一起来看看 《大型网站技术架构》 这本书的介绍吧!