
在 go 中,多个 goroutine 同时读写同一数组(即使读操作可容忍脏数据)属于数据竞争,违反内存安全模型,必须通过 sync.mutex 等同步机制保护。
在 go 中,多个 goroutine 同时读写同一数组(即使读操作可容忍脏数据)属于数据竞争,违反内存安全模型,必须通过 sync.mutex 等同步机制保护。
Go 的内存模型明确规定:对同一变量的并发读写(至少一个是写操作)构成数据竞争(data race),其行为未定义——即使你声称“不关心读到部分更新的值”,也无法规避底层硬件缓存不一致、编译器重排序或运行时优化引发的崩溃、静默错误或不可重现的异常。
以原始代码为例:
var arr [100]byte go ReadFrom(arr) // 传值!实际复制整个数组 go ReadFrom(arr) go WriteTo(arr)
⚠️ 注意:此处存在一个关键误解——ReadFrom(arr) 和 WriteTo(arr) 中的 arr 是按值传递的。这意味着每个 goroutine 操作的是独立副本,而非共享底层数组。因此,这段代码看似“安全”实则无效:WriteTo 修改的是自己的副本,对其他 goroutine 中的 arr 完全无影响;而两个 ReadFrom 读取的也只是初始零值副本。
✅ 正确做法是传递指针,并配合互斥锁保护共享状态:
package main
import (
"sync"
"time"
)
var (
arr [100]byte
mu sync.Mutex
)
func ReadFrom(data *[100]byte) {
for range time.Tick(100 * time.Millisecond) {
mu.Lock()
// 安全读取:拷贝当前快照(避免持有锁过久)
snapshot := *data
mu.Unlock()
// 在锁外处理数据(如解析、日志等)
_ = snapshot[0] // 示例使用
}
}
func WriteTo(data *[100]byte) {
i := 0
for range time.Tick(50 * time.Millisecond) {
mu.Lock()
data[i%100] = byte(i)
i++
mu.Unlock()
}
}
func main() {
go ReadFrom(&arr)
go ReadFrom(&arr)
go WriteTo(&arr)
time.Sleep(3 * time.Second) // 保持主 goroutine 运行
}
? 关键要点:
- *必须传指针 `[100]byte`** 才能实现真正的共享;
- 读写均需加锁,即使读操作允许“旧值”,也不能跳过同步——否则仍触发 data race;
- 避免在锁内执行耗时操作(如 I/O、复杂计算),推荐锁内仅做最小原子读/写,必要时拷贝后处理;
- 使用
go run -race main.go编译运行可自动检测数据竞争,强烈建议在开发与 CI 中启用。
总结:Go 不提供隐式线程安全,数组不是例外。共享可变状态必须显式同步——sync.Mutex 是最直接、可靠的选择。










