我对goroutine感到困惑,使我感到困惑。
首先,在Go1.12
环境中执行以下代码
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for {
x++
}
}()
}
time.Sleep(time.Second)
fmt.Println("x = ", x)
}
我无法获得结果,并且主要过程完全没有响应。
然后有人告诉我将环境更改为Go1.14
,我做到了并得到结果:x = 0
,但我期望的结果是x等于一个大于0的数字。
然后我尝试在goroutine中添加打印日志,如下所示:
func main() {
// ... code ...
go func() {
for {
fmt.Println("exec goroutine")
x++
}
}()
// ... code ...
}
我得到了我期望的结果,x = 61827
(还有很多字符串exec goroutine
)。
我的问题:
关于上述问题,Go1.12和Go1.14有什么区别?
为什么在goroutine中添加打印日志后为什么我得到正确的结果?
编辑:有关特定的go版本,请参阅@Volker的评论
如果仅使用一个cpu进行操作(如无限循环),则它可能会保留调度程序(因此您的主要功能永远不会运行到结束)
print
与可以释放调度程序的time.Sleep(time.Millisecond)
相同
Goroutine正在并行执行代码。第一个要理解的概念是线程安全性。 (即使goroutine不是线程,概念是相同的)
[运行代码时,您可以对同一变量x
进行多重访问,但是由于操作x++
或get操作不是原子的,因此不同的goroutine可能看不到相同的值变量。
一些资源可以帮助您了解:*How to make a variable thread-safe* https://en.wikipedia.org/wiki/Thread_safety
有多种方法可以解决您的问题
原子操作:
func main() {
var x uint64
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for {
atomic.AddUint64(&x, 1)
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
fmt.Println("x = ", x)
}
Mutex允许您同步对变量的访问,因此您将看到正确的值:
unc main() {
var x int
var mutex = &sync.Mutex{}
for i := 0; i < threads; i++ {
go func() {
for {
mutex.lock()
x++
mutex.Unlock()
time.Sleep(time.Millisecond)
}
}()
}
time.Sleep(time.Second)
mutex.lock()
fmt.Println("x = ", x)
mutex.Unlock()
}