【问题标题】:Golang concurrency write to variable - why this code works?Golang并发写入变量 - 为什么这段代码有效?
【发布时间】:2019-05-23 11:19:53
【问题描述】:

我正在学习 Golang 中与并发相关的问题。我写了一些代码:

package main

import (
    "fmt"
    "time"
)

func incr(num *int) {
    *num = *num + 1

}

func main() {
    var a = 0

    for i := 0; i < 50; i++ {
        go incr(&a)
    }

    incr(&a)

    time.Sleep(1 * time.Second)
    fmt.Println(a)
}

这段代码的结果是:51

在这段代码中,我声明了 a 变量,我在 50 个正在运行的 goroutine 中增加了该变量。我读过和理解的这段代码应该会失败,因为多个 goroutine 正在写入同一个内存地址。在这种情况下,我应该添加sync.Mutex lock 来解决这个问题。

代码在 Playground 中可用:https://play.golang.org/p/Tba9pfpxaHY

你能解释一下这个程序到底发生了什么吗?

【问题讨论】:

  • 它不起作用。你刚刚得到了“幸运”。也许您正在单 CPU 机器上运行(例如 Go 游乐场)?使用竞赛检测器 (go run -race) 运行它。
  • IMO,这就是为什么并发编程更难的原因 :) 没有明显的错误或恐慌,可以在观察下工作。虽然竞态检测器极有可能检测到这种特定的竞态条件,但在某些条件/情况下,竞态检测器不会检测到任何东西。这就是 IMO 我们仍然需要回归第一原则并学习识别所有并发访问 (medium.com/dm03514-tech-blog/…) 并默认保护的地方

标签: go concurrency locking mutex shared-memory


【解决方案1】:

你猜怎么着?我运行了你的应用程序,我得到了不同的输出:有时是49,,有时是48,有时是50(有时是51)。

如果您在启用竞争检测器 (go run -race play.go) 的情况下运行您的应用,它会告诉您有数据竞争:

==================
WARNING: DATA RACE
Read at 0x00c00009a010 by goroutine 7:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x3a

Previous write at 0x00c00009a010 by goroutine 6:
  main.incr()
      /home/icza/gows/src/play/play.go:9 +0x50

Goroutine 7 (running) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83

Goroutine 6 (finished) created at:
  main.main()
      /home/icza/gows/src/play/play.go:17 +0x83
==================

当您遇到数据竞争时,您的应用的行为是未定义。 “有时似乎工作”也适合“未定义”行为,但未定义也意味着它也可以做任何其他事情。

查看相关问题:

Assign a map to another map is safety in golang?

Is it safe to read a function pointer concurrently without a lock?

golang struct concurrent read and write without Lock is also running ok?

【讨论】:

  • 我还会指点 this classic piece 由那个种族检测器背后的人之一 ;-)
  • @kostix 是的,该链接也包含在我的链接答案之一中,但多做广告也无妨。
猜你喜欢
  • 2014-02-02
  • 2021-06-20
  • 1970-01-01
  • 1970-01-01
  • 2017-07-12
  • 2012-12-27
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
相关资源
最近更新 更多