在Golang并发编程里,当多个Goroutine在没有同步保护的情况下访问同一个变量并且至少有一个是写操作时,就会产生数据竞争(race condition)。这种问题往往难以复现,却可能让程序输出错误结果或发生崩溃。理解竞争原因并掌握检测与解决方法,是写好Go并发程序的基础。

什么是Goroutine间的数据竞争
数据竞争发生在两个或更多Goroutine并发访问同一内存位置,且至少一个是写入,并且没有使用任何同步机制来协调访问顺序。例如下面的代码,主Goroutine和启动的Goroutine同时修改全局变量count:
package main
import (
"fmt"
"time"
)
var count int
func main() {
go func() {
for i := 0; i < 1000; i++ {
count = count + 1
}
}()
for i := 0; i < 1000; i++ {
count = count + 1
}
time.Sleep(time.Second)
fmt.Println("count =", count)
}
上面的程序每次运行打印的count可能都不一样,因为count = count + 1不是原子操作,读取、加一、写回三步可能被交错执行。
如何使用Golang检测race condition
Golang工具链内置了race检测器,基于Google的ThreadSanitizer。编译或测试时加上-race标志即可启用。
使用go run -race
对上面的示例用race检测:
go run -race main.go
如果存在竞争,终端会输出WARNING: DATA RACE以及具体的读写栈信息,指出发生竞争的代码行。
使用go test -race
在编写单元测试时,推荐始终开启race:
go test -race ./...
这样可以在CI中自动捕获并发缺陷。
Golang解决数据竞争的常见方法
方法一:使用sync.Mutex互斥锁
通过互斥锁保证同一时间只有一个Goroutine能进入临界区:
package main
import (
"fmt"
"sync"
"time"
)
var (
count int
mu sync.Mutex
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
mu.Lock()
count = count + 1
mu.Unlock()
}
}()
}
wg.Wait()
fmt.Println("count =", count)
}
方法二:使用channel传递数据
Go提倡通过通信共享内存,而不是通过共享内存来通信。可以用通道把计数任务串行化:
package main
import "fmt"
func main() {
count := 0
ch := make(chan int, 1)
ch <- 0
done := make(chan struct{})
for i := 0; i < 2; i++ {
go func() {
for j := 0; j < 1000; j++ {
v := <-ch
v++
ch <- v
}
done <- struct{}{}
}()
}
for i := 0; i < 2; i++ {
<-done
}
count = <-ch
fmt.Println("count =", count)
}
方法三:使用sync/atomic原子操作
对于简单计数器,atomic包性能更好:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var count int64
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 1000; j++ {
atomic.AddInt64(&count, 1)
}
}()
}
wg.Wait()
fmt.Println("count =", atomic.LoadInt64(&count))
}
总结建议
在Golang中处理Goroutine间的数据竞争,第一步是用-race工具定位问题,第二步是根据场景选择互斥锁、通道或原子操作。日常开发中,尽量把共享状态限制在最小范围,多用通道传递所有权,能显著降低竞争风险。
Golangrace_conditiongoroutine修改时间:2026-07-28 09:06:22