【问题标题】:A problem about golang concurrency in oncegolang并发一次的问题
【发布时间】:2020-06-17 19:15:30
【问题描述】:

我在看官方文档,有一段代码看不懂。

var a string
var done bool

func setup() {
    a = "hello, world"
    done = true
}

func doprint() {
    if !done {
        once.Do(setup)
    }
    print(a)
}

func twoprint() {
    go doprint()
    go doprint()
}

这个版本可以(错误地)打印一个空字符串而不是“hello, world”。

为什么这段代码会产生错误的结果。

【问题讨论】:

  • 这段代码来自哪里?这没有任何意义 - once 没有被声明为开始。
  • @AJR 代码取自The Go Memory Modeloncesync.Once 的一个实例。压力在于不正确的同步。
  • @icza 在那种情况下我明白了。令人困惑的是,特定问题是done 上的数据竞争,与once 无关。顺便说一句,我赞成您的回答并删除了我愚蠢的回答。

标签: go concurrency


【解决方案1】:

因为在doprint() 中,done 变量在没有同步的情况下被访问(读取)。

想象第一个运行 doprint() 的 goroutine 在第二个运行之前完成。第一个写入done(在setup() 中),第二个goroutine 尝试读取done:读取是否会产生第一个goroutine 写入的结果?没有同步:未定义的行为。可能done 将被观察为true,因此不会调用setup(),也可能不会观察到对a 的写入,因此初始零值(空字符串)为打印出来的。

对变量的读取总是在一个单个 goroutine 中观察之前的写入(时间上的之前)。一个 goroutine 的写入不能保证在另一个 goroutine 中被观察到,除非它们之间有同步。

【讨论】:

    【解决方案2】:

    为了进一步解释公认的答案,在 Go 中,编译器和处理器可能会重新排序读/写。在单个 go 例程中,保证这种重新排序不会影响行为。

    但是,在您的示例中,有多个没有同步的 go 例程,并且重新排序确实会产生影响。假设第一个 goroutine 执行 done=true 然后 a=xxxx,如果第二个 goroutine 在第一个 done=truea=xxxx 之前执行,它将产生一个空字符串。

    【讨论】:

      【解决方案3】:

      @icza 已经回答了这个问题。在考虑这些类型的问题时,我会添加一个提示——想象一下 goroutines 是同时启动的。是的,代码一个接一个地开始,但是延迟(如果有的话)太小了,以至于出于推理的目的,考虑它们同时执行会使缺陷显而易见:

      如果两者同时运行,哪一个会将 bool 设置为 true?

      如果你不能自信地识别出这个 goroutine,那么你就暴露了这个缺陷!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-08
        • 1970-01-01
        • 2017-12-05
        相关资源
        最近更新 更多