当前位置:首页 > 文章列表 > Golang > Go教程 > Go 怎样用 atomic.Uint64 记录无锁计数

Go 怎样用 atomic.Uint64 记录无锁计数

来源:17golang原创 2026-10-18 04:00:11 0浏览 收藏

多个 goroutine 累计已完成事件时,可以把数值放进 atomic.Uint64,用 Add(1) 更新、用 Load() 读取。这里的“无锁计数”指调用代码不再为这一个数显式包一把互斥锁;它不承诺所有平台上的进度特性,也不代表整个业务没有同步成本。

从普通整数改成原子类型,收益先是减少误用

Go 1.19 起提供类型化原子整数。对维护请求累计量、成功事件数的服务而言,把操作收拢到字段的方法上,有助于避免同一个地址一处原子访问、另一处普通读写的混用。这是接口层面的变化,不是本文掌握了某种行业采用率数据。

先限定计数口径:一次 Inc 对应一个已经确定要计入的事件;读取者只需要一个独立数值,不需要同时核对订单状态或另一项余额。若后者必须与计数一致,单独换一个原子字段仍不够。

第一步:给计数器提供窄接口

下面的完整示例要求 Go 1.19 或更高版本。字段不导出,方法使用指针接收者,调用方只能累计和取快照。示例代码未在本文制作过程中执行,后面的数字是按代码逻辑给出的预期。

package main

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

// Counter 使用后不能复制,所有调用方共享其指针。
type Counter struct {
    n atomic.Uint64
}

// Inc 用一次原子读改写累计事件,返回更新后的值。
func (c *Counter) Inc() uint64 {
    return c.n.Add(1)
}

// Value 仅返回该计数器在一次读取时的快照。
func (c *Counter) Value() uint64 {
    return c.n.Load()
}

func main() {
    c := new(Counter) // 零值可用,不需要另行初始化。
    var wg sync.WaitGroup
    const workers = 4
    const each = 1000

    wg.Add(workers)
    for i := 0; i 

Inc 的返回值属于这一次增量完成时的新值。如果其他 goroutine 紧接着又累计,返回值不会随之改变;因此它不是一个持续更新的引用。当前封装没有暴露清零接口,避免监控消费者把“累计计数”误读成可随时重置的状态量。

共享计数器与原子访问的静态结构
图1:计数对象、累计入口与快照读取的静态关系;不是线程调度或运行截图。

第二步:共享对象指针,再区分快照与最终总数

示例中四个任务拿到的是同一个 *Counter,并不是四份计数对象。每个任务累计1000次,等待它们全部结束后,在没有其他写入者且没有回绕的条件下,预期输出为4000。这个最终值既需要原子累计不丢增量,也需要 WaitGroup 把读取放在任务结束之后。

如果把读取放到任务仍在执行的时候,读到的中间数量也可以是正确快照。不能仅凭它小于4000就断言丢计数,更不能用反复读取计数替代任务完成协议。采集器适合读快照,负责收尾的调用方则应等待自己管理的任务结束。

第三步:不要把 Load 加 Store 当成 Add

// 反例:两次原子操作并不组成一次原子加法。
func wrongIncrement(n *atomic.Uint64) {
    old := n.Load()
    n.Store(old + 1) // 并发调用可能覆盖别人的增量。
}

假设两个调用都读到7,它们可能分别写入8;虽然每次读取、写入都是原子的,两次业务增量仍然只留下一个增量。这里缺少的是一次不可拆开的读改写,而不是给两条语句分别贴上原子标签。正确累计直接使用 Add(1),不要先取出旧值再写回。

同样,先判断计数小于阈值再调用 Add,不能自动实现并发名额限制。本文只做事件累计;涉及条件更新的业务需要另行定义整体同步规则,不把它塞进这个最小计数接口。

第四步:禁止复制与64位对齐要分别处理

已经使用的计数器不能通过结构体赋值、值参数、值接收者或装入按值存储的容器而复制。复制外层结构体也会复制里面的原子字段。需要共享时传指针,需要导出报表时只取 Value() 得到普通 uint64;报表数值与在线计数对象有不同的生命周期。

对齐是另一个维度。在 ARM、386 和32位 MIPS 上,旧式底层64位原子函数要求调用者安排地址的64位对齐;类型化的 atomic.Uint64 则自动满足其对齐要求。正常声明这种字段即可,不要为了“节省字节”把字节缓冲区通过 unsafe 强转成原子计数地址,也不要照抄标准库内部的空结构来冒充编译器识别的对齐机制。

计数器身份与保证范围的静态结构
图2:对象身份、对齐与业务保证范围;分组表示边界,不表示执行先后。

逐步采用时,观察热点而不是预设更快

迁移可以先选一个独立、单调累计的指标,统一入口,再替换读取端;不要只改写入端而保留旧的普通字段读取。若原来一把锁保护多个有关联字段,拆掉它之前必须重新说明这些字段的一致性要求。

高频并发写入同一个数仍可能形成热点,原子操作并不会消除缓存一致性开销。是否值得采用,应在自己的负载上比较吞吐、延迟以及写入竞争;本文不提供未经实测的加速比例,也不展开分片计数方案。

最后要约定数值范围:uint64 的加法按无符号整数规则回绕,不会替你报告业务计数溢出。若导出的累计值允许重启归零、存在清零或回绕可能,消费端不能毫无条件地把相邻快照相减当成新增量。单个字段的原子性也不能自动给多个计数器提供同一时刻的整体快照。

实际采用边界可以压缩为一句话:只统计独立事件,共享同一对象,累计用 Add、取值用 Load;对象身份、任务完成和业务一致性仍由调用方负责。

官方文档入口

类型和方法说明:https://pkg.go.dev/sync/atomic。类型实现与对齐标记说明:https://go.dev/src/sync/atomic/type.go。本文的图片仅帮助对应正文结构,不作为性能或运行结果证据。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 怎样读取文件中的固定区间而不移动共享游标Go 怎样读取文件中的固定区间而不移动共享游标
上一篇
Go 怎样读取文件中的固定区间而不移动共享游标
Go 解析相对请求目标失败怎么办
下一篇
Go 解析相对请求目标失败怎么办
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    512次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    572次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    597次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    549次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    365次使用