Golang中为什么for循环对i++不起作用?
Golang中为什么for循环对i++不起作用?
package main
import (
"fmt"
"time"
)
func main() {
var x int
go func() {
for {
x++
}
}()
time.Sleep(time.Second)
fmt.Println("x =", x) // x = 0, why?
}
谢谢 🙂
4 回复
time.Sleep() 难道不应该给其他 goroutine 运行的机会吗?
更多关于Golang中为什么for循环对i++不起作用?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
因为你的 goroutine 从未有机会运行。在调度器调度它之前,main 函数就已经退出了。你需要某种同步机制,比如通道或 sync.WaitGroup。
func main() {
fmt.Println("hello world")
}
这是因为你的代码中存在数据竞争(data race)。在Go语言中,当多个goroutine并发访问同一个变量,且至少有一个是写操作时,如果没有正确的同步机制,就会发生数据竞争。
在你的例子中:
- 主goroutine和匿名goroutine同时访问变量
x - 匿名goroutine不断执行
x++(写操作) - 主goroutine在1秒后读取
x的值 - 两个goroutine之间没有使用任何同步机制
问题根源:
- Go的内存模型不保证一个goroutine的写入对另一个goroutine立即可见
- 编译器/CPU可能会对指令进行重排序优化
- 变量
x可能被缓存在CPU寄存器中,而不是立即写回内存
正确的解决方案: 使用同步原语来确保可见性和原子性
示例1:使用sync/atomic包(推荐)
package main
import (
"fmt"
"sync/atomic"
"time"
)
func main() {
var x int64 // 必须使用int64,因为atomic.AddInt64需要
go func() {
for {
atomic.AddInt64(&x, 1)
}
}()
time.Sleep(time.Second)
fmt.Println("x =", atomic.LoadInt64(&x))
}
示例2:使用sync.Mutex
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var (
x int
mu sync.Mutex
)
go func() {
for {
mu.Lock()
x++
mu.Unlock()
}
}()
time.Sleep(time.Second)
mu.Lock()
fmt.Println("x =", x)
mu.Unlock()
}
示例3:使用channel进行同步
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
x := 0
for {
x++
select {
case ch <- x:
// 成功发送
default:
// 如果主goroutine还没准备好接收,就继续循环
}
}
}()
time.Sleep(time.Second)
select {
case x := <-ch:
fmt.Println("x =", x)
default:
fmt.Println("no value received")
}
}
关键点:
- 使用
sync/atomic包进行原子操作 - 使用
sync.Mutex或sync.RWMutex进行互斥访问 - 使用channel进行goroutine间通信
- 运行程序时可以使用
go run -race main.go来检测数据竞争
在你的原始代码中,由于没有同步机制,主goroutine可能看到的是x的初始值(0),或者缓存中的旧值,而不是匿名goroutine更新后的值。

