
在 Go 中,多个 goroutine 同时读写同一数组(如 [100]byte)属于数据竞争行为,即使允许读取“部分更新”的值,也无法保证数组内容完整性;必须使用 sync.Mutex 等同步机制保障安全。
在 go 中,多个 goroutine 同时读写同一数组(如 `[100]byte`)属于数据竞争行为,即使允许读取“部分更新”的值,也无法保证数组内容完整性;必须使用 `sync.mutex` 等同步机制保障安全。
Go 的内存模型明确规定:对同一变量的非同步读写操作构成数据竞争(data race),而数组(即使是值类型)在作为参数传递给函数时,若未显式复制其底层数据,实际共享同一块内存地址。虽然 var arr [100]byte 是值类型,但当你将其作为参数传入 ReadFrom(arr) 和 WriteTo(arr) 时,Go 默认按值传递——这意味着每次调用都会创建数组的完整副本。⚠️ 然而,这恰恰是常见误解的关键点:
- ✅
ReadFrom(arr)和WriteTo(arr)接收的是arr的副本,因此原始arr不会被这些函数直接修改; - ❌ 但你的代码意图显然是让三个 goroutine 并发访问同一个底层存储(否则
WriteTo的修改对ReadFrom不可见,失去意义); - ? 实际上,若
WriteTo需要修改原数组,它必须接收指针(如*[100]byte),此时所有 goroutine 共享同一地址 —— 这就构成了典型的数据竞争。
正确做法是:显式共享底层数组指针,并用 sync.Mutex 保护临界区。示例如下:
package main
import (
"sync"
"time"
)
var arr [100]byte
var mu sync.Mutex
func ReadFrom(data *[100]byte) {
for range time.Tick(100 * time.Millisecond) {
mu.Lock()
// 安全读取:拷贝一份快照,避免锁持有过久
snapshot := *data
mu.Unlock()
// 在锁外处理数据(如打印、校验)
_ = snapshot // 示例中忽略具体逻辑
}
}
func WriteTo(data *[100]byte) {
for range time.Tick(200 * time.Millisecond) {
mu.Lock()
// 安全写入:逐字节或批量修改
for i := range data {
data[i] = byte(i % 256)
}
mu.Unlock()
}
}
func main() {
go ReadFrom(&arr)
go ReadFrom(&arr)
go WriteTo(&arr)
time.Sleep(2 * time.Second) // 保持主 goroutine 活跃
}? 关键注意事项:
- 始终对共享变量使用指针(
*[N]T)配合sync.Mutex,而非值传递; - 读操作也需加锁(哪怕只读),因为写操作可能正在修改内存,无锁读取可能导致字节级撕裂(tearing),破坏“所有值 intact”的要求;
- 锁粒度宜粗不宜细:本例中整数组共用一把锁;若性能成瓶颈,可考虑分段锁(sharded mutex)或
sync.RWMutex(读多写少场景); - 开发阶段务必启用竞态检测器:
go run -race main.go,它能自动捕获此类隐患。
总结:Go 不提供隐式线程安全,“不关心中间态”不等于“无需同步”——只要存在并发读+写同一内存位置,就必须显式同步。sync.Mutex 是最直接、可靠且符合 Go 信条(Share memory by communicating)的基础方案。

















