为什么此示例可能无法退出?

时间:2019-08-15 05:53:52

标签: go memory

这是Go文档中"The Go Memory Model"

的示例

代码:

var a string
var done bool

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

func main() {
    go setup()
    for !done {
    }
    print(a)
}

文档中的原语:

和以前一样,不能保证在主体上观察完成的写入意味着观察对a的写入,因此该程序也可以打印空字符串。更糟糕的是,由于两个线程之间没有同步事件,因此无法保证main会一直执行写入操作。不能保证main中的循环会结束。

为什么?

我想主要的goroutine只是在寄存器中缓存 done = false 变量,而没有任何同步操作来刷新寄存器缓存。

谢谢。


编辑2019-08-16

我知道了。

如果设置了runtime.GOMAXPROCS(1),则唯一的线程将始终运行for !done{}。调度程序没有任何机会将线程上下文切换到setup() subgoroutine。因此,编译器无法保证此示例完成。

当我们调用内置lib函数时,运行时lib将有机会调度goroutine。例如:

mutex.Lock()
// 1、enter go build-in lib
// 2、switch to other goroutine if sync condition is not satisfied
// 3、condition is satisfied
someAction()
mutex.Unlock()

2 个答案:

答案 0 :(得分:3)

您有一场数据竞赛。结果是不确定的。

$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000052b581 by goroutine 6:
  main.setup()
      /home/peter/gopath/src/racer.go:8 +0x70

Previous read at 0x00000052b581 by main goroutine:
  main.main()
      /home/peter/gopath/src/racer.go:13 +0x56

Goroutine 6 (running) created at:
  main.main()
      /home/peter/gopath/src/racer.go:12 +0x46
==================
==================
WARNING: DATA RACE
Read at 0x000000510220 by main goroutine:
  main.main()
      /home/peter/gopath/src/racer.go:15 +0x74

Previous write at 0x000000510220 by goroutine 6:
  main.setup()
      /home/peter/gopath/src/racer.go:7 +0x3e

Goroutine 6 (finished) created at:
  main.main()
      /home/peter/gopath/src/racer.go:12 +0x46
==================
hello, worldFound 2 data race(s)
exit status 66
$ 

racer.go

package main

var a string
var done bool

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

func main() {
    go setup()
    for !done {
    }
    print(a)
}

done = true!done不同步是一场数据竞赛。

a = "hello, world"print(a)不同步是一场数据竞赛。


Go: Data Race Detector

答案 1 :(得分:-3)

当您使用go关键字运行一个函数时,它将打开一个新的goroutine,编译器将查看此goroutine并说:

'嘿,在此goroutine中,将done设置为true和a被设置为hello world完全不相关,因此我可以对它们进行排序,但这对我来说是最有效的。'

除非其他goroutine之间没有特定的链接,否则编译器不会查看它们。通道或锁。

这就是为什么它可能从不打印a的原因,因为可以先将done设置为true,以便for循环退出并在其他goroutine设置a之前穿过打印行。