0

0

如何在Golang中测试并发数据访问_Golang sync/atomic与Benchmark方法

P粉602998670

P粉602998670

发布时间:2026-01-21 08:15:34

|

251人浏览过

|

来源于php中文网

原创

直接用int在goroutine里计数会出错,因为i++是非原子的read-modify-write操作,多goroutine并发执行时可能读到相同旧值并写回相同新值,导致结果丢失;必须用sync/atomic(如AddInt64)或sync.Mutex保证原子性。

如何在golang中测试并发数据访问_golang sync/atomic与benchmark方法

为什么直接用 intgoroutine 里计数会出错

因为 int 的读写不是原子操作。多个 goroutine 同时执行 i++(即 read-modify-write)时,可能读到同一个旧值、各自加 1、再写回,导致最终结果比预期少。这不是“偶尔出错”,而是只要并发足够高、调度足够乱,就必然发生。

典型错误现象:

func TestCounterRace(t *testing.T) {
    var i int
    var wg sync.WaitGroup
    for range [1000]int{} {
        wg.Add(1)
        go func() {
            defer wg.Done()
            i++ // 这里有数据竞争
        }()
    }
    wg.Wait()
    if i != 1000 {
        t.Errorf("expected 1000, got %d", i) // 几乎必 fail
    }
}

  • 运行 go test -race 会立刻报出 Data race on variable i
  • 即使没开 race detector,结果也常是 992、997 等非 1000 值
  • sync.Mutex 能解决,但有锁开销;sync/atomic 更轻量,适合简单整型操作

sync/atomic 支持哪些类型和操作

sync/atomic 不支持任意类型,只对底层可原子操作的整型和指针提供封装:基本是 int32int64uint32uint64uintptr*unsafe.Pointer。注意:int 在 32 位系统上是 int32,64 位上是 int64 —— 但 atomic 不提供 int 版本函数,必须显式选 int32int64

  • atomic.AddInt64(&i, 1):返回新值,线程安全自增
  • atomic.LoadInt64(&i):安全读取当前值
  • atomic.StoreInt64(&i, 42):安全写入
  • atomic.CompareAndSwapInt64(&i, old, new):CAS,成功返回 true
  • 没有 atomic.IncInt64,只有 AddInt64(x, 1)

别用 int 变量配 atomic.AddInt64 —— 类型不匹配会编译失败:

var i int
atomic.AddInt64(&i, 1) // ❌ compile error: cannot use &i (type *int) as type *int64

怎么写一个靠谱的 Benchmark 测原子操作开销

基准测试要避免被编译器优化掉,也要控制变量。比如测 atomic.AddInt64 vs mutex vs 普通 ++(后者仅作对比,实际不能用),关键点是:所有操作必须作用在可逃逸的变量上,且结果要被使用(如赋给 b.ReportMetric 或全局变量),否则会被优化为无操作。

立即学习go语言免费学习笔记(深入)”;

Anyword
Anyword

AI文案写作助手和文本生成器,具有可预测结果的文案 AI

下载
  • b.N 控制循环次数,不是硬写 1000000
  • 避免在循环内创建新 goroutine —— Benchmark 是单 goroutine 下的吞吐测算,不是压测并发行为
  • 想测并发场景下的性能?得用 go test -benchmem -benchtime=3s 并手动启 goroutine + sync.WaitGroup,但要注意结果解释:它测的是「N 个 goroutine 争抢一个原子变量」的吞吐,不是单次操作延迟

一个干净的单 goroutine 基准示例:

func BenchmarkAtomicAdd(b *testing.B) {
    var i int64
    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        atomic.AddInt64(&i, 1)
    }
    // 强制使用 i,防止优化
    blackhole = i
}

var blackhole int64

atomic 在真实服务中容易被忽略的边界

很多人以为用了 atomic 就万事大吉,但几个关键限制常被忽视:

  • atomic 只保证单个操作原子性,不保证多字段间的一致性。比如你有两个 int64 字段 countsum,分别用 atomic 更新,但业务要求「每次 count+1 必须伴随 sum+=x」——这时仍需锁或更高级结构(如 atomic.Value 存整个 struct 指针)
  • atomic.Value 只支持 Store/Load,且存的值必须是相同类型;存 *MyStruct 后不能再存 *bytes.Buffer
  • 32 位 ARM 上,atomic.LoadUint64 等 64 位操作需要硬件支持,老设备可能 panic;生产环境建议统一用 int64 并确认目标平台支持
  • 没有 atomic.MinInt64atomic.MaxUint32 —— 这类操作得靠 CompareAndSwap 循环实现,写错容易死循环

真正复杂的共享状态,atomic 往往只是拼图一角。它快,但不够“聪明”。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

180

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

340

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

209

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

392

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

197

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

191

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

212

2025.06.17

Java JVM 原理与性能调优实战
Java JVM 原理与性能调优实战

本专题系统讲解 Java 虚拟机(JVM)的核心工作原理与性能调优方法,包括 JVM 内存结构、对象创建与回收流程、垃圾回收器(Serial、CMS、G1、ZGC)对比分析、常见内存泄漏与性能瓶颈排查,以及 JVM 参数调优与监控工具(jstat、jmap、jvisualvm)的实战使用。通过真实案例,帮助学习者掌握 Java 应用在生产环境中的性能分析与优化能力。

19

2026.01.20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号