当前位置:首页 > 文章列表 > Golang > Go教程 > atomic类型实现无锁计数器

atomic类型实现无锁计数器

来源:17golang原创 2026-11-15 05:00:08 0浏览 收藏

Go 可以用 atomic.Uint64 的 Add(1) 实现并发计数,再用 Load() 读取总数。业务代码无需给单个数值的递增加互斥锁,但必须共享同一个计数器实例,且不能把一次原子操作误认为整个业务流程都具备原子性。

官方文档:https://pkg.go.dev/sync/atomic

项目目标:统计一组并发任务的完成次数

小项目只做一件事:启动6个 goroutine,每个递增700次,任务全部完成后打印最终总数。这里的“无锁计数器”指计数字段使用原子操作、业务侧不为递增显式持有 Mutex;它不是对整个程序、运行时调度或所有平台底层实现作出的实时性保证。

普通整数的 total++ 包含读取、加一、写回。多个 goroutine 无同步地修改同一整数会发生数据竞争,也可能丢失更新。换成 Load 再 Store 同样不能让这两次调用合成一次不可分割的递增,应直接使用 Add。

环境准备:使用带类型的原子数值

atomic.Uint64 在 Go 1.19 加入,零值即可作为初始为0的计数器。项目不需要第三方依赖,保存为 main.go 后即可用 Go 工具链运行。字段封装进 Counter,外部通过指针接收者方法访问,便于保持同一份状态。

WaitGroup 负责等待任务结束,Counter 负责数值更新。这两个对象有不同职责:Load 能读到一个原子的当前值,却不会等待尚未执行的递增。最终汇总需要明确的任务完成条件。

并发任务、共享Counter和WaitGroup的静态关系
结构说明图:任务共享一个Counter实例,WaitGroup独立维护任务完成关系;连线不是执行时序。

核心代码:递增用 Add,读取用 Load

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

// Counter 使用后不复制,所有调用都共享同一个实例。
type Counter struct {
    total atomic.Uint64
}

func (c *Counter) Inc() {
    c.total.Add(1) // 用一次原子读改写完成递增。
}

func (c *Counter) Value() uint64 {
    return c.total.Load() // 并发读取也经过原子接口。
}

func main() {
    const workers, each = 6, 700
    counter := &Counter{}
    var wg sync.WaitGroup
    wg.Add(workers) // 在启动任务前登记,避免与 Wait 形成错误时序。

    for i := 0; i 

Counter 没有额外构造函数,因为原子字段零值已经可用。Inc 和 Value 都是指针接收者,调用不会复制正在使用的字段。各 goroutine 收到的也是同一个 counter 指针,而不是各自独立的 Counter 值。

wg.Add 在任务启动前登记数量,每个任务用 defer wg.Done 表达完成,主函数 Wait 返回后才打印汇总。示例没有任何真实业务失败分支;接入业务后应明确什么才算“完成”,避免把任务启动、尝试次数和成功次数混在一起。

本地运行:按任务数量推导预期结果

# 在保存 main.go 的目录运行这个独立示例。
go run main.go

# 可用竞态检测器辅助发现实际执行路径上的数据竞争。
go run -race main.go

按代码语义,预期两项数字都是4200,即 total=4200 expected=4200。这是预期结果说明,本文没有把它描述成本机运行日志。竞态检测不能证明所有业务路径都正确,也不能发现“两个原子操作合起来不符合业务约束”这类逻辑问题。

运行中如果提前调用 Value,读到较小值也不一定意味着更新丢失,可能只是其他任务还没有完成。因此结果是否符合预期,需要同时看读取位置和等待条件,不能只看计数字段是否用了 atomic。

部署与集成:避免复制,也别把复合条件拆开

官方要求 Uint64 首次使用后不能复制。包含它的 Counter 同样应按不可复制对象管理:不要用值接收者,不要把运行中的 Counter 按值传参,也不要对含有它的业务结构体随意作值拷贝。对外输出数字快照,用 Value 返回的 uint64,而不是把整个对象复制出去。

如果业务是“当前数量低于上限才加一”,先 Load 判断再 Add 会留下并发窗口,多个任务都可能通过判断。可以为这种单字段条件更新另行设计 CompareAndSwap 重试,或直接使用互斥锁保护完整条件。不要把本文只递增的 Inc 生搬到额度控制上。

两个原子字段分别保存成功数与总数,也不意味着两个 Load 构成一致快照。需要跨字段不变量或同时更新多个对象时,Mutex 往往更容易表达。若多个任务高频争用同一数值,原子操作仍有共享写入成本;是否更快,应按真实负载测量,本文不提供未经测量的倍数。

单次原子更新与复合业务条件的职责边界
结构说明图:Add和Load提供单次操作语义;等待、条件更新与多字段一致性分别需要自己的设计。

长期累加还要考虑 uint64 的范围。溢出不会替你生成业务错误,不能无限期把单个固定宽度计数器当作永不耗尽的账本。需要允许负数的增减场景,可以重新评估 atomic.Int64 与字段范围,而不是绕过类型约束。

验收清单:看状态共享和业务语义

检查项本例做法容易混淆的地方
共享对象传递同一个Counter指针每个任务创建自己的计数器
并发递增Add(1)Load后Store不等于原子递增
读取总数Load()读取不代表等待任务完成
最终结果Wait后汇总提前读取不一定丢失更新
多个字段另行设计同步边界多个原子调用不等于事务

Add 返回什么?返回加法完成后的新值;若需要本次递增得到的序号,可接收这个返回值,仍要考虑溢出和业务唯一性范围。

是否所有地方都该用 atomic?不必。它适合独立数值状态;有复杂条件和关联数据时应选择更清楚的同步方式。

能直接打印Counter作为快照吗?不要依赖对整个含原子字段对象的复制或格式化来表达并发快照,先调用 Value 取得普通整数再输出。

参考:atomic.Uint64 标准库文档;sync.WaitGroup 文档。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
WaitGroupGo启动受控并发任务WaitGroupGo启动受控并发任务
上一篇
WaitGroupGo启动受控并发任务
timeTicker管理周期任务
下一篇
timeTicker管理周期任务
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1033次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1032次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1175次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1136次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    979次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码