You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何原子指针赋值仍被检测到数据竞争?

Go代码竞态检测问题解析

问题代码

package main

import (
    "fmt"
    "time"
)

func main() {
    str := "ab"
    strPtr := &str
    go func() {
        str2 := "cd"
        strPtr = &str2
    }()
    time.Sleep(2 * time.Second)
    fmt.Printf("current address %p\n", strPtr)
}

竞态检测报告

==================
WARNING: DATA RACE
Read at 0x00c0000b4018 by main goroutine:
  main.main()
      /Users/***/main/main.go:16 +0x124

Previous write at 0x00c0000b4018 by goroutine 7:
  main.main.func1()
      /Users/***/main/main.go:13 +0x7c

Goroutine 7 (finished) created at:
  main.main()
      /Users/***/main/main.go:11 +0x110
==================
current address 0xc000180000
Found 1 data race(s)
exit status 66

疑问与解答

疑问1:为何报告的读取地址和最终输出地址不一致?

竞态报告里的0x00c0000b4018是变量strPtr自身的内存地址,不是它指向的字符串的地址。

  • 主goroutine执行fmt.Printf时,会读取strPtr变量本身存储的值(也就是指针内容);
  • 子goroutine则在修改strPtr变量本身的值(把它从指向str改成指向str2)。
  • 这两个对strPtr的读、写操作在无同步的情况下并发执行,因此触发了数据竞争。

而程序输出的0xc000180000是strPtr最终指向的str2的地址,和strPtr自身的地址完全是两个不同的内存位置,所以才会出现看起来“地址不一致”的情况。

疑问2:指针赋值是原子操作,为什么还会检测到数据竞争?

Go内存模型中提到的“不大于机器字长的访问是原子操作”,仅表示单次读/写不会被拆分成多个步骤,不会出现读取到半更新值的情况,但这和“数据竞争”是两个完全不同的概念。

数据竞争的核心判定标准是:两个不同goroutine同时访问同一个变量,且至少有一个操作是写操作,同时没有使用同步原语(如互斥锁、通道)来协调访问顺序。即使指针赋值是原子的,只要读和写没有同步机制约束,就属于数据竞争场景——竞态检测器的目标就是捕捉这类无同步的并发读写,不管操作是否原子。

内容的提问来源于stack exchange,提问作者Michal Hosala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 10:55:34