简体   繁体   English

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

[英]Why code in loop not executed when I have two go-routines

I'm facing a problem in golang 我在golang遇到问题

var a = 0
func main() {
        go func() {
                for {
                        a = a + 1
                }
        }()
        time.Sleep(time.Second)
        fmt.Printf("result=%d\n", a)
}
  • expected: result=(a big int number) 预期:结果=(大整数)
  • result: result=0 结果:result = 0

You have a race condition, run your program with -race flag 您有竞争条件,请使用-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

what is solution? 什么是解决方案?
There is some solution, A solution is using a mutex: 有一些解决方案,一种解决方案是使用互斥锁:

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()
}

before any read and write lock the mutex and then unlock it, now you don not have any race and resault will bi big int at the end. 在进行任何读写锁定互斥锁然后将其解锁之前,现在您没有任何竞争,因此重新加载将在最后结束。
For more information read this topic. 有关更多信息,请阅读本主题。
Data races in Go(Golang) and how to fix them and this Go(Golang)中的数据竞赛及其解决方法

Golang concurrency - data races Golang并发-数据竞赛

Your program is running into race condition. 您的程序正在进入竞争状态。 go can detect such scenarios. go可以检测到这种情况。

Try running your program using go run -race main.go assuming your file name is main.go . 假设您的文件名为main.go请尝试使用go run -race main.go运行程序。 It will show how race occured , attempted write inside the goroutine , simultaneous read by the main goroutine. 它将显示种族如何发生,如何在goroutine中写入,如何通过主goroutine同时读取。 It will also print a random int number as you expected. 它也会按照您的预期打印一个随机的int数。

As other writers have mentioned, you have a data race, but if you are comparing this behavior to, say, a program written in C using pthreads, you are missing some important data. 正如其他作者所提到的,您有一个数据竞赛,但是如果将此行为与使用pthreads用C编写的程序进行比较,则会丢失一些重要数据。 Your problem is not just about timing, it's about the very language definition. 您的问题不仅在于时间安排,还在于语言定义。 Because concurrency primitives are baked into the language itself, the Go language memory model ( https://golang.org/ref/mem ) describes exactly when and how changes in one goroutine -- think of goroutines as "super-lightweight user-space threads" and you won't be too far off -- are guaranteed to be visible to code running in another goroutine. 因为并发原语是语言本身所固有的,所以Go语言内存模型( https://golang.org/ref/mem )准确描述了一个goroutine的更改时间和更改方式-将goroutines视为“超轻量用户空间”线程”,而且距离不会太远-保证对在另一个goroutine中运行的代码可见。

Without any synchronizing actions, like channel sends/receives or sync.Mutex locks/unlocks, the Go memory model says that any changes you make to 'a' inside that goroutine don't ever have to be visible to the main goroutine. 在没有任何同步动作的情况下,例如通道发送/接收或同步.Mutex锁定/解锁,Go内存模型表明,您对主goroutine中对'a'所做的任何更改不必对主​​goroutine可见。 And, since the compiler knows that, it is free to optimize away pretty much everything in your for loop. 而且,由于编译器知道这一点,因此可以自由地优化for循环中的几乎所有内容。 Or not. 或不。

It's a similar situation to when you have, say, a local int variable in C set to 1, and maybe you have a while loop reading that variable in a loop waiting for it to be set to 0 by an ISR, but then your compiler gets too clever and decides to optimize away the test for zero because it thinks your variable can't ever change within the loop and you really just wanted an infinite loop, and so you have to declare the variable as volatile to fix the 'bug'. 这类似于您将C中的局部int变量设置为1的情况,也许您有一个while循环在循环中读取该变量,等待ISR将其设置为0,然后再进行编译,变得太聪明了,因此决定将测试优化为零,因为它认为您的变量永远无法在循环内更改,而您实际上只是想进行无限循环,因此您必须将变量声明为volatile才能修复“ bug” 。

If you are going to be working in Go, (my current favorite language, FWIW,) take time to read and thoroughly grok the Go memory model linked above, and it will really pay off in the future. 如果您要使用Go(我目前最喜欢的语言,FWIW)进行工作,则需要花一些时间阅读并彻底理解上面链接的Go内存模型,它将在将来真正获得回报。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM