go - 数据竞争,两个 goroutine 加上相同的 val
问题描述
考虑下面的代码,在我看来,val 将在 100 到 200 之间,但它总是 200
var val = 0
func main() {
num := runtime.NumCPU()
fmt.Println("使用cpu数量", num)
go add("A")
go add("B")
time.Sleep(1 * time.Second)
fmt.Println("val的最终结果", val)
}
func add(proc string) {
for i := 0; i < 100; i++ {
val++
fmt.Printf("execute process[%s] and val is %d\n", proc, val)
time.Sleep(5 * time.Millisecond)
}
}
为什么 val 最后总是 200?
解决方案
您的代码有两个问题:
- 你有一个数据竞争——
val
在没有同步的情况下同时写入和读取。它的存在使得对程序结果的推理变得毫无意义。 - 1 秒的睡眠
main()
时间太短 - 1 秒后 goroutines 可能还没有完成。您根本不希望fmt.Printf
花费任何时间,但控制台输出确实需要大量时间(在某些操作系统上比其他操作系统长)。所以循环不会花费 100 * 5 = 500 毫秒,而是要长得多。
这是一个固定版本,它以原子方式递增val
并正确等待两个 goroutine 完成,而不是假设它们将在 1 秒内完成。
var val = int32(0)
func main() {
num := runtime.NumCPU()
fmt.Println("使用cpu数量", num)
var wg sync.WaitGroup
wg.Add(2)
go add("A", &wg)
go add("B", &wg)
wg.Wait()
fmt.Println("val的最终结果", atomic.LoadInt32(&val))
}
func add(proc string, wg *sync.WaitGroup) {
for i := 0; i < 100; i++ {
tmp := atomic.AddInt32(&val, 1)
fmt.Printf("execute process[%s] and val is %d\n", proc, tmp)
time.Sleep(5 * time.Millisecond)
}
wg.Done()
}
推荐阅读
- python - 附加后尝试在 DataFrame 中的切片副本上设置值
- math - 为什么指定轨道运动需要近点论证?
- kubernetes - 尝试在 ibm Kubernetes 集群中安装 Tiller 时,出现错误,因为 Tiller pod 未准备好
- fb-hydra - 使用显式嵌套专门化 Hydra 配置
- python - 如何在 Python 中更改日期格式?
- perl - 仅从文档的开头和结尾修剪空间,而不使用 shell 脚本中的 perl 触及内部空间
- javascript - Node.js Express 服务器未打开静态文件
- java - Apache HTTP 客户端下载 SOAP 响应的一部分
- python - 如何在不使用 inbuild 函数的情况下对两个列表进行排序,请解释我收到此错误的原因
- substrate - 如何通过 FRAME v2 解决“Offchain-worker”comile 错误