当前位置:首页 > 文章列表 > Golang > Go教程 > Go atomic.Int64 Add 返回值如何用于无锁序号生成

Go atomic.Int64 Add 返回值如何用于无锁序号生成

来源:17golang原创 2026-09-14 17:44:19 0浏览 收藏

如果多个 goroutine 需要领取业务序号,最小写法不是“先 Load 再 Add”,而是直接使用 atomic.Int64.Add(1) 的返回值。这个返回值是加法完成后的新值,因此每次调用都能拿到一个不会重复的序号;代码层面不需要 sync.Mutex。但它只保证原子递增,不保证 goroutine 的启动顺序、完成顺序,也不适合直接当作跨进程全局 ID。

要点速览
  • Add(delta) 返回新值,Add(1) 可直接作为下一个序号。
  • 原子唯一不等于业务有序;并发任务可能先拿到 2、后完成 1。
  • 不要复制已经使用过的 atomic.Int64,还要提前规划溢出和失败重试。

最小配方:让 Add(1) 直接产出新序号

atomic.Int64 的零值就是 0。第一次调用 Add(1) 返回 1,第二次返回 2。不要把它写成先读取再普通相加,因为那会把“读取、计算、写回”拆成多个并发步骤。

package sequence

import "sync/atomic"

// IDGenerator 只保存下一个可分配序号的计数状态。
type IDGenerator struct {
	next atomic.Int64
}

// Next 返回本次调用独占的新序号,从 1 开始。
func (g *IDGenerator) Next() int64 {
	// Add 返回递增后的值,正好就是本次领取的序号。
	return g.next.Add(1)
}

这个结构体可以作为服务对象的字段长期持有。方法接收指针,避免把包含原子状态的对象复制一份。图 1 用静态关系框图展示了 IDGeneratoratomic.Int64Add(1) 与返回序号之间的边界关系。

Go atomic.Int64 Add 生成序号的静态模块关系示意图
图1:操作示意图,展示 IDGenerator、atomic.Int64 与 Add(1) 返回新序号的静态关系。

为什么不要用 Load 加一再 Store

下面这种写法每一步单独看都像是原子操作,组合起来却不是一次不可分割的递增:

// 错误示例:两个 goroutine 可能同时读到相同旧值。
old := g.next.Load()
newValue := old + 1 // 这里没有把计算和写回绑定成一个原子动作。
g.next.Store(newValue)
return newValue

两个调用可能同时读到 7,然后都计算出 8,最终返回重复的编号。Add(1) 把加法和返回值绑定在同一个原子操作中,调用者不需要自己拼接临界区。Go 官方文档还说明,原子操作在程序中表现为顺序一致的操作序列;这解决的是共享计数状态的并发可见性,不是整个业务流程的事务性。

Add 返回唯一值,但不承诺任务完成顺序

假设两个 goroutine 同时调用 Next,它们拿到 10 和 11 是确定的;但拿到 10 的任务可能因为网络请求更慢,最后才写入数据库。不要把序号当作时间戳,也不要用它推断日志先后。

需求Add(1) 能否满足需要补充的设计
并发调用不重复可以所有领取都走同一个生成器实例
按完成时间连续不可以用队列、提交阶段或显式排序
服务重启后继续唯一单靠内存不可以持久化状态或外部发号服务

如果业务允许“领取即消耗”,某个任务失败后留下空洞是正常结果。若必须重试时复用同一个编号,就要把编号和任务状态一起持久化,而不是再次调用 Add(1)

Go 原子序号与任务顺序持久化边界静态关系示意图
图2:结果示意图,展示原子计数、并发任务和持久化提交之间的静态边界;它不是实际运行截图。

四个容易忽略的边界

  • 不要复制:atomic.Int64 第一次使用后不能再复制。把生成器放进容器时保存指针,或在初始化完成后只移动未使用的值。
  • 留意溢出:有符号整数到达上限后继续递增会回绕。序号接近上限时应切换区间或停止发号,不能把负数继续当作正 ID。
  • 统一实例:每个请求临时创建一个生成器都会从 0 开始,得到的只是局部序号;共享生成器才能在同一进程内避免重复。
  • 明确范围:atomic.Int64 只保护进程内共享内存。多副本服务要使用数据库自增列、Redis 原子命令或专门的 ID 服务。

常见问题

Add 返回的是旧值还是新值?

是新值。计数器为 4 时调用 Add(1),返回 5;如果需要旧值,要另行设计交换或减法逻辑,不能凭经验倒推。

用 atomic.Int64 就一定比互斥锁快吗?

不能这样保证。原子递增适合短小的共享状态更新,但完整业务仍可能受网络、分配和持久化影响;是否更快应以目标平台的基准测试为准。

序号必须严格按请求到达顺序怎么办?

不要只依赖 Add。先定义“到达”的判定点,再用串行队列或带确认的提交流程决定顺序;原子计数器只负责安全地领取一个数。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
PHP ReflectionMethod invokeArgs 使用命名参数时有什么限制PHP ReflectionMethod invokeArgs 使用命名参数时有什么限制
上一篇
PHP ReflectionMethod invokeArgs 使用命名参数时有什么限制
Java Locale 默认值改变后字符串格式化为什么不稳定
下一篇
Java Locale 默认值改变后字符串格式化为什么不稳定
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    23次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    126次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    51次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    21次使用
  • OpenCompass大模型评测体系详解:功能、使用指南与应用场景
    OpenCompass
    OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
    74次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码