当我有两个go例程时,为什么不执行循环中的代码

当我有两个go例程时,为什么不执行循环中的代码,go,goroutine,Go,Goroutine,我在golang面临一个问题 var a=0 func main(){ go func(){ 为了{ a=a+1 } }() 时间。睡眠(时间。秒) fmt.Printf(“结果=%d\n”,a) } 预期:结果=(一个大整数) 结果:结果=0 您的程序正在进入竞速状态go可以检测到这种情况 尝试使用go-run-race main.go运行程序,假设文件名为main.go。它将展示种族是如何发生的, 尝试在goroutine中写入, 由主goroutine同时读取。 它还将按照您的预期打印

我在golang面临一个问题

var a=0
func main(){
go func(){
为了{
a=a+1
}
}()
时间。睡眠(时间。秒)
fmt.Printf(“结果=%d\n”,a)
}
  • 预期:结果=(一个大整数)
  • 结果:结果=0

您的程序正在进入竞速状态
go
可以检测到这种情况

尝试使用
go-run-race main.go运行程序,假设文件名为
main.go
。它将展示种族是如何发生的, 尝试在goroutine中写入, 由主goroutine同时读取。 它还将按照您的预期打印一个随机整数。

您有一个竞赛条件, 使用-race标志运行程序

go run -race main.go
==================
WARNING: DATA RACE
Read at 0x0000005e9600 by main goroutine:
  main.main()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:17 +0x6c

Previous write at 0x0000005e9600 by goroutine 6:
  main.main.func1()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:13 +0x56

Goroutine 6 (running) created at:
  main.main()
      /home/jack/Project/GoProject/src/gitlab.com/hooshyar/GoNetworkLab/StackOVerflow/race/main.go:11 +0x46
==================
result=119657339
Found 1 data race(s)
exit status 66

什么是解决方案?
有一些解决方案,一个解决方案是使用互斥:

var a = 0
func main() {
    var mu sync.Mutex

    go func() {
        for {
            mu.Lock()
            a = a + 1
            mu.Unlock()
        }
    }()
    time.Sleep(3*time.Second)
    mu.Lock()
    fmt.Printf("result=%d\n", a)
    mu.Unlock()
}
在进行任何读写操作之前,先锁定互斥锁,然后解锁互斥锁,现在您没有任何竞争,而Result将在最后执行bi big int。
有关更多信息,请阅读本主题。
还有这个


正如其他作者所提到的,您有一个数据竞争,但是如果您将此行为与(例如)使用pthreads用C编写的程序进行比较,您会丢失一些重要的数据。你的问题不只是时间,而是语言定义本身。由于并发原语被烘焙到语言本身中,Go语言内存模型()准确地描述了一个goroutine中的更改(将goroutine视为“超轻量用户空间线程”,并且您不会离它太远)在何时以及如何确保在另一个goroutine中运行的代码可见

没有任何同步操作,如通道发送/接收或sync.Mutex锁定/解锁,Go内存模型表示,您对goroutine中的“a”所做的任何更改都不必对主goroutine可见。而且,因为编译器知道这一点,所以可以自由地优化for循环中的几乎所有内容。或者不是

这与C中的一个局部int变量设置为1时的情况类似,可能有一个while循环在循环中读取该变量,等待ISR将其设置为0,但是你的编译器变得太聪明了,决定优化掉零测试,因为它认为你的变量在循环中永远不会改变,你真的只是想要一个无限循环,所以你必须将变量声明为volatile来修复“bug”


如果你打算在Go(我目前最喜欢的语言,FWIW)中工作,花点时间阅读并彻底探索上面链接的Go内存模型,它将在未来获得回报。

你有一场数据竞赛,因此任何结果都是未定义的。忙循环也没有帮助,因为它总是会干扰执行。谢谢你的解释。然而,当在c中运行完全相同的代码时,我得到了不同的结果(如使用互斥体的解决方案)。这是go例程的一个特性还是bug?对于处理器来说,1秒是一个很长的周期,但结果仍然是0。这是否意味着在此期间,第二个例程从未将变量的值传递给主例程?即使在go编写的代码中,每次运行代码的结果都会不同,在c程序中出现另一个结果也不是很少见。我得到的结果=53413251不是零。。在go版本go1.12.5 darwin/amd64中,我总是0,甚至睡了10秒钟。@justbeatdruid:这并不奇怪,你有一个数据竞赛,所以输出可以是任何东西:()。此外,由于对
a
的读写之间没有同步,编译器和cpu可以根据自己的需要自由地重新排序。FWIW@justbeatdruid,如果您真的想运行这个精确的代码,并且您关心性能,那么您应该与原子同步(请参阅)而不是在每次循环迭代中锁定/解锁互斥锁。但是Ali是绝对正确的——他的代码可以工作,并且修复了数据竞争,而性能并不是真正的重点。您需要理解当一个goroutine中的更改被语言保证在另一个goroutine中可见时的规则(请参阅我的答案以获得稍微长一点的解释)