如何在Golang中测试并发数据访问_Golang sync/atomic与Benchmark方法
技术百科
P粉602998670
发布时间:2026-01-21
浏览: 次 直接用int在goroutine里计数会出错,因为i++是非原子的read-modify-write操作,多goroutine并发执行时可能读到相同旧值并写回相同新值,导致结果丢失;必须用sync/atomic(如AddInt64)或sync.Mutex保证原子性。
为什么直接用 int 在 goroutine 里计数会出错
因为 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 不支持任意类型,只对底层可原子操作的整型和指针提供封装:基本是 int32、int64、uint32、uint64、uintptr 和 *unsafe.Pointer。注意:int 在 32 位系统上是 int32,64 位上是 int64 —— 但 atomic 不提供 int 版本函数,必须显式选 int32 或 int64。
-
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 或全局变量),否则会被优化为无操作。
- 用
b.N控制循环次数,不是硬写 1000000 - 避免在循环内创建新 goroutine ——
Benchmark是单 goroutine 下的吞吐测算,不是压测并发行为 - 想测并发场景下的性能?得用
go test -benchmem -benchtime=3s并手动启 goroutine +sync.WaitGroup,但要注意结果解释:它测的是「N 个 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字段count和sum,分别用atomic更新,但业务要求「每次 count+1 必须伴随 sum+=x」——这时仍需锁或更高级结构(如atomic.Value存整个 struct 指针) -
atomic.Value只支持Store/Load,且存的值必须是相同类型;存*MyStruct后不能再存*bytes.Buffer - 32 位 ARM 上,
atomic.LoadUint64等 64 位操作需要硬件支持,老设备可能 panic;生产环境建议统一用int64并确认目标平台支持 - 没有
atomic.MinInt64或atomic.MaxUint32—— 这类操作得靠CompareAndSwap循环实现,写错容易死循环
真正复杂的共享状态,atomic 往往只是拼图一角。它快,但不够“聪明”。
# ai
# 的是
# 几个
# 很多人
# 多个
# 也要
# 多字
# 你有
# go
# golang
# 循环
# 并发
# int
# 指针
# 为什么
# 线程
# pointer
# 封装
# Struct
# cad
# 万事大吉
# count
# 整型
# 数据访问
# 全局变量
# 读到
# 有锁
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win11怎么开启游戏工具栏_Windows11
- Windows怎样关闭开始菜单广告_Windows
- 网站内页做seo排名怎么做?
- php8.4新语法match怎么用_php8.4m
- Flask 表单数据通过 SMTP 发送邮件的完整
- Win11怎么设置默认图片查看器_Windows1
- Linux如何安装Tomcat应用服务器_Linu
- Windows蓝屏错误0x0000002C怎么解决
- Windows 11怎么设置默认解压软件_Wind
- Windows10怎么用“讲述人”读屏辅助 Win
- C++友元类使用场景_C++类间协作设计方式讲解
- Win11怎么关闭键盘按键音_Win11禁用打字声
- Win11怎么更改默认打开方式_Win11关联文件
- Python多进程教程_multiprocessi
- VSC里PHP变量未定义报错怎么解决_错误抑制技巧
- c++如何获取map中所有的键_C++遍历键值对提
- c# 服务器GC和工作站GC的区别和设置
- Python并发安全问题_资源竞争说明【指导】
- Win10路由器怎么隐藏ssid Win10隐藏w
- Win11鼠标灵敏度怎么调 Win11鼠标指针移动
- Windows 11怎么更改锁屏超时时间_Wind
- Win11怎么开启智能存储_Windows11存储
- 如何在Windows中创建新的用户账户?(标准与管
- MAC怎么在照片中添加水印_MAC自带编辑工具文字
- Win11怎么设置闹钟_Windows 11时钟应
- Windows系统文件被保护机制阻止怎么办_权限不
- VSC怎样在VSC中调试PHPAPI_接口调试技巧
- Win10如何更改网络连接_Windows10以太
- Mac如何解压zip和rar文件?(推荐免费工具)
- Win11怎么设置ip地址_Windows 11手
- Windows资源管理器总是卡顿或重启怎么办?(修
- c++中如何对数组进行排序_c++数组排序算法汇总
- Win10如何卸载预装Edge扩展_Win10卸载
- 如何在Golang中处理模块包路径变化_Golan
- Win11怎么关闭透明效果_Windows11辅助
- Bpmn 2.0的XML文件怎么画流程图
- php订单日志怎么记录评价_php记录订单评价日志
- Win10怎么创建桌面快捷方式 Win10为应用创
- c++中的std::conjunction和std
- Win10文件历史记录怎么用 Win10开启自动备
- Win11蓝牙开关不见了怎么办_Win11蓝牙驱动
- Win11怎么查看局域网电脑_Windows 11
- MAC怎么截图并快速编辑_MAC自带截图快捷键与标
- Win11怎么关闭触控板_Win11笔记本禁用触摸
- c++ try_emplace用法_c++ map
- Win11如何设置系统语言_Win11系统语言切换
- 如何将竖排文本文件转换为横排字符串
- Mac如何设置动态壁纸?(让桌面动起来)
- 如何在 Django 中安全修改用户密码而不使会话
- Win10如何更改任务栏高度_Windows10解

QQ客服