Golang调试与运行结果不一致的奇怪现象
Golang调试与运行结果不一致的奇怪现象
package main
import (
"fmt"
)
var x int32 = 1
func main() {
go foo()
for {
fmt.Printf("%d\n", x)
}
}
func foo() {
for i := 0; ; i++ {
if i%2 == 0 {
x = 2
} else {
x = 3
}
}
}

更多关于Golang调试与运行结果不一致的奇怪现象的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
你在并行运行两个紧密循环。执行顺序是任意的,每次都可能改变。如果两者由不同的系统线程执行,操作系统调度器将决定执行的顺序和优先级。它们可能在不同的核心上运行,也可能在同一个核心上运行。
从两个并发的 Go 协程并发访问同一个变量是不安全的——如果你使用 -race 标志运行代码,你会收到一个警告。
由于对 X 的访问没有安全的“前/后”顺序,编译器可能会进行优化,并将 X 的值作为循环不变量在本地缓存——因此循环会假设 X 永远保持为 1,从而永远只打印 1(这对于规范来说是正确的行为)。
我认为调试器可能不会使用这种优化,因此你“幸运地”在输出中得到了一个随机的 2/3 字符串。
这是一个典型的并发数据竞争问题。在Go语言中,当多个goroutine同时读写共享变量而没有同步机制时,就会出现这种调试和运行结果不一致的现象。
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// 方案1: 使用原子操作
var x int32 = 1
func main() {
go foo()
for {
fmt.Printf("%d\n", atomic.LoadInt32(&x))
}
}
func foo() {
for i := 0; ; i++ {
if i%2 == 0 {
atomic.StoreInt32(&x, 2)
} else {
atomic.StoreInt32(&x, 3)
}
}
}
package main
import (
"fmt"
"sync"
)
// 方案2: 使用互斥锁
var (
x int32 = 1
mu sync.RWMutex
)
func main() {
go foo()
for {
mu.RLock()
fmt.Printf("%d\n", x)
mu.RUnlock()
}
}
func foo() {
for i := 0; ; i++ {
mu.Lock()
if i%2 == 0 {
x = 2
} else {
x = 3
}
mu.Unlock()
}
}
package main
import (
"fmt"
)
// 方案3: 使用通道
func main() {
ch := make(chan int32)
go foo(ch)
for val := range ch {
fmt.Printf("%d\n", val)
}
}
func foo(ch chan<- int32) {
for i := 0; ; i++ {
if i%2 == 0 {
ch <- 2
} else {
ch <- 3
}
}
}
使用go run -race命令可以检测数据竞争:
go run -race main.go
编译器优化和CPU缓存一致性可能导致不同运行环境下看到不同的值。在调试器中,执行速度的差异可能暂时掩盖了数据竞争,但在实际运行时问题就会暴露出来。

