Go 是一个内置支持并发编程的语言。借助使用 go
关键字去创建 协程 goroutine (轻量级线程)和在 Go 中提供的 使用 信道 和 其它的并发 同步方法,使得并发编程变得很容易、很灵活和很有趣。
另一方面,Go 并不会阻止一些因 Go 程序员粗心大意或者缺乏经验而造成的并发编程错误。在本文的下面部分将展示一些在 Go 编程中常见的并发编程错误,以帮助 Go 程序员们避免再犯类似的错误。
需要同步的时候没有同步
代码行或许 不是按出现的顺序运行的。
在下面的程序中有两个错误。
- 第一,在
main
协程中读取b
和在新的 协程 中写入b
可能导致数据争用。 - 第二,条件
b == true
并不能保证在main
协程 中的a != nil
。在新的协程中编译器和 CPU 可能会通过 重排序指令 进行优化,因此,在运行时b
赋值可能发生在a
赋值之前,在main
协程 中当a
被修改后,它将会让部分a
一直保持为nil
。
package main
import (
"time"
"runtime"
)
func main() {
var a []int // nil
var b bool // false
// a new goroutine
go func () {
a = make([]int, 3)
b = true // write b
}()
for !b { // read b
time.Sleep(time.Second)
runtime.Gosched()
}
a[0], a[1], a[2] = 0, 1, 2 // might panic
}
上面的程序或者在一台计算机上运行的很好,但是在另一台上可能会引发异常。或者它可能运行了 N 次都很好,但是可能在第 (N+1) 次引发了异常。
我们将使用 sync
标准包中提供的信道或者同步方法去确保内存中的顺序。例如,
package main
func main() {
var a []int = nil
c := make(chan struct{})
// a new goroutine
go func () {
a = make([]int, 3)
c