两个Go协程并行写入切片同一元素是否会触发panic?
Go协程并发写入切片同一元素是否会触发panic?
核心结论
这种场景下几乎不会触发panic,但属于明确的数据竞争行为,程序行为会进入未定义状态——最常见的表现是数据错乱,但不能绝对排除极端情况下出现异常的可能。
具体解释
- 切片元素的赋值(比如
arr[2] = "xxx")本质是对底层数组对应内存位置的写入操作。对于string类型来说,写入的是字符串的指针和长度两个字段,这两个操作并非原子操作。 - 当多个协程并发写入同一内存位置,同时还有协程读取时,属于Go内存模型中明确禁止的数据竞争场景。这种情况下程序的行为没有保障:
- 最常见的结果是数据错乱:比如读协程可能读到一半写入的字符串(指针更新了但长度没更新,或者反过来),导致输出异常内容;或者两个写协程的操作互相覆盖,出现不符合预期的时间戳。
- 触发panic的概率极低,但并非完全不可能:极端情况下如果内存操作出现异常(比如非法指针),理论上可能引发panic,但在实际测试中几乎不会遇到。
对测试代码的说明
你提供的测试代码运行后,大概率不会panic,但用go run -race命令执行时,会明确检测到数据竞争问题。这类代码绝对不应该出现在生产环境中,必须通过同步机制(比如sync.Mutex)来保护共享数据的访问,示例修改如下:
import ( "fmt" "strconv" "sync" "time" ) func main() { arr := []string{"a", "b", "c", "d"} var mu sync.Mutex // write 1 go func() { for { mu.Lock() arr[2] = strconv.FormatInt(time.Now().Unix(), 10) mu.Unlock() } }() // read 1 go func() { for { mu.Lock() fmt.Println(arr[2]) mu.Unlock() } }() // write 2 go func() { for { mu.Lock() arr[2] = strconv.FormatInt(time.Now().Unix(), 10) mu.Unlock() } }() time.Sleep(10 * time.Second) return }
内容的提问来源于stack exchange,提问作者daidai21
相关产品推荐
相关产品推荐

