我偶然发现了一个有问题的 Golang 代码,该代码试图使用互斥锁来防止更改 goroutine 中打印的变量:
runtime.GOMAXPROCS(1)
mutex := new(sync.Mutex)
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
mutex.Lock()
go func() {
fmt.Println(i, j, i + j);
mutex.Unlock()
}()
}
}
我很清楚 mutex 不会直接锁定,而是在下一次迭代时,当值已经递增时。不清楚的是为什么根据输出j变量达到10:
...
0 7 7
0 8 8
0 9 9
1 10 11 <--- isn't supposed to be here
...
1 9 10
2 10 12
...
我尝试调试代码,当 i 的外循环递增其值时打印了 j = 10
。看起来好像外层循环正在释放线程以允许 goroutine 执行并看到无效值 10。有人可以澄清这种行为吗?
最佳答案
您有数据竞争。结果未定义。
$ go run -race racer.go
==================
WARNING: DATA RACE
Read at 0x00c000016110 by goroutine 7:
main.main.func1()
/home/peter/gopath/racer.go:17 +0x7f
Previous write at 0x00c000016110 by main goroutine:
main.main()
/home/peter/gopath/racer.go:14 +0xf1
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/racer.go:16 +0xcd
==================
0 1 1
0 2 2
0 3 3
0 4 4
0 5 5
0 6 6
0 7 7
0 8 8
0 9 9
==================
WARNING: DATA RACE
Read at 0x00c000016108 by goroutine 16:
main.main.func1()
/home/peter/gopath/racer.go:17 +0x50
Previous write at 0x00c000016108 by main goroutine:
main.main()
/home/peter/gopath/racer.go:13 +0x140
Goroutine 16 (running) created at:
main.main()
/home/peter/gopath/racer.go:16 +0xcd
==================
1 10 11
1 1 2
1 2 3
1 3 4
1 4 5
1 5 6
1 6 7
1 7 8
1 8 9
1 9 10
2 10 12
2 1 3
2 2 4
2 3 5
2 4 6
2 5 7
2 6 8
2 7 9
2 8 10
2 9 11
3 10 13
3 1 4
3 2 5
3 3 6
3 4 7
3 5 8
3 6 9
3 7 10
3 8 11
3 9 12
4 10 14
4 1 5
4 2 6
4 3 7
4 4 8
4 5 9
4 6 10
4 7 11
4 8 12
4 9 13
5 10 15
5 1 6
5 2 7
5 3 8
5 4 9
5 5 10
5 6 11
5 7 12
5 8 13
5 9 14
6 10 16
6 1 7
6 2 8
6 3 9
6 4 10
6 5 11
6 6 12
6 7 13
6 8 14
6 9 15
7 10 17
7 1 8
7 2 9
7 3 10
7 4 11
7 5 12
7 6 13
7 7 14
7 8 15
7 9 16
8 10 18
8 1 9
8 2 10
8 3 11
8 4 12
8 5 13
8 6 14
8 7 15
8 8 16
8 9 17
9 10 19
9 1 10
9 2 11
9 3 12
9 4 13
9 5 14
9 6 15
9 7 16
9 8 17
9 9 18
Found 2 data race(s)
exit status 66
$
racer.go
:
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
runtime.GOMAXPROCS(1)
mutex := new(sync.Mutex)
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
mutex.Lock()
go func() {
fmt.Println(i, j, i+j)
mutex.Unlock()
}()
}
}
}
关于go - 观察循环内的无效值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57620363/