我在golang中遇到问题
var a = 0
func main() {
go func() {
for {
a = a + 1
}
}()
time.Sleep(time.Second)
fmt.Printf("result=%d\n", a)
}
答案 0 :(得分:2)
您有比赛条件, 使用-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()
}
在任何读写锁定互斥锁然后将其解锁之前,现在您没有任何竞争,并且重新加载最终将成为一个大整数。
有关更多信息,请阅读本主题。
Data races in Go(Golang) and how to fix them
还有这个
答案 1 :(得分:0)
您的程序正在进入竞争状态。 go
可以检测到这种情况。
假设您的文件名为go run -race main.go
,请尝试使用main.go
运行程序。它将显示种族是如何发生的,
尝试在goroutine中写入,
由主goroutine同时读取。
还会按照您的期望打印一个随机的int数。
答案 2 :(得分:0)
正如其他作者所提到的,您有一个数据竞赛,但是如果将此行为与使用pthreads用C编写的程序进行比较,则会丢失一些重要数据。您的问题不仅在于时间安排,还在于语言定义。因为并发原语是语言本身所固有的,所以Go语言内存模型(https://golang.org/ref/mem)准确描述了一个goroutine中的更改时间和方式-将goroutines视为“超轻量级用户空间线程”,您将'距离不要太远-保证对在另一个goroutine中运行的代码可见。
没有任何同步动作,例如通道发送/接收或同步.Mutex锁定/解锁,Go内存模型说您对goroutine中的'a'所做的任何更改都不会永远对主goroutine可见。而且,由于编译器知道这一点,因此可以自由地优化for循环中的几乎所有内容。或不。
这与您将C中的局部int变量设置为1时类似,并且可能有一个while循环在循环中读取该变量,等待ISR将其设置为0,但是,然后您的编译器变得太聪明了,并决定将测试优化为零,因为它会认为,您的变量永远不会在循环内更改,而您实际上只是想要一个无限循环,因此您必须声明变量为volatile
来修复'bug'。
如果您要使用Go(我目前最喜欢的语言,FWIW)进行工作,则需要花一些时间阅读并彻底弄清上面链接的Go内存模型,将来它将真正获得回报。