此代码显示0,但是如果我插入time.Sleep(0)到更新程序中 循环,它显示> 1
var Nonce int = 0
func Updater(){
for{
Nonce += 1
}
}
func main(){
go Updater()
time.Sleep(time.Second)
fmt.Printf("%d\n",Nonce)
}
答案 0 :(得分:2)
nonce.go
:
package main import ( "fmt" "time" ) var Nonce int = 0 func Updater() { for { Nonce += 1 } } func main() { go Updater() time.Sleep(time.Second) fmt.Printf("%d\n", Nonce) }
首先,Go gc编译器是优化的编译器。 goroutine之间没有同步。因此,Nonce += 1
语句被忽略,Nonce
的值保持为零。查看编译后的代码:
$ go build nonce.go
$ objdump -d -S ./nonce
输出:
var Nonce int = 0
func Updater() {
for {
Nonce += 1
4888f0: eb fe jmp 4888f0 <main.Updater>
4888f2: cc int3
}
}
第二,如果我们运行Go数据竞争检测器,则会抑制某些优化。因此,检测到Nonce
变量的数据争用条件。数据争用的结果是不确定的。
$ go run -race nonce.go
==================
WARNING: DATA RACE
Read at 0x0000005f2648 by main goroutine:
main.main()
/home/peter/gopath/src/nonce.go:19 +0x63
Previous write at 0x0000005f2648 by goroutine 6:
main.Updater()
/home/peter/gopath/src/nonce.go:12 +0x56
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/nonce.go:17 +0x46
==================
42758109
Found 1 data race(s)
exit status 66
$
答案 1 :(得分:0)
您的main
函数启动一个goroutine来运行Updater
,然后立即退出。没有睡眠,Updater
没有时间开始做自己的事情。这段代码是 racy -它有时会按您期望的那样工作,有时却不会。您必须以某种方式使Updater
与main
同步,例如使用通道或等待组。
此外,您将在一个goroutine中更新Nonce
全局,而在另一个goroutine中读取它-这是一场数据竞赛。您需要使用互斥锁来同步对此var的访问。
这是代码的一种更正确的变体,尽管它仍然很荒谬(为什么您需要一个goroutine来运行忙循环?)
package main
import (
"fmt"
"sync"
)
var Nonce int = 0
func Updater(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 1000; i++ {
Nonce += 1
}
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go Updater(&wg)
wg.Wait()
fmt.Printf("%d\n", Nonce)
}
这里,我们对WaitGroup
使用Updater
来向主goroutine发出“我已完成”信号,然后该主goroutine才检查Nonce
的值。该程序每次应打印“ 1000”