Go atomic.AndUint64 怎样原子清除状态位
来源:17golang原创
时间:2026-11-01 15:55:49 275浏览 收藏
要原子清除 uint64 状态字中的某几位,关键不是把待清除位直接传给 atomic.AndUint64,而是先取反得到保留掩码:keepMask := ^bitsToClear。然后执行 oldState := atomic.AndUint64(&state, keepMask)。这样按位 AND 会把目标位变成 0,同时保留其他位;返回值 oldState 是更新前的状态快照。
官方文档:https://pkg.go.dev/sync/atomic#AndUint64
如果要清除的状态位是bitsToClear,就传入^bitsToClear。不要直接传待清除位,否则保留下来的位会被一起清零。
先把状态位和反掩码分清
可以把一个 uint64 看成一组并发状态标志。例如最低位表示在线,第二位表示有错误:
package main import "sync/atomic" const ( // 用独立的位表示不同状态,便于组合成一个状态字。 stateOnline uint64 = 1
这里的 stateError 是“要清除的位”,^stateError 才是“要保留的位”。因为 AND 0 的结果一定是 0,AND 1 才能保持原值。

调用 AndUint64 并读取旧状态
AndUint64 的签名是 func AndUint64(addr *uint64, mask uint64) (old uint64)。它对 *addr 做原子按位 AND,并返回旧值。返回旧值很有用:它能让调用方在一次原子更新中判断“清除之前”某个标志是否存在。
func clearFlags(state *uint64, bitsToClear uint64) (oldState uint64, wasSet bool) {
// 反掩码中的 1 表示保留,0 表示清除对应状态位。
keepMask := ^bitsToClear
// oldState 是更新前快照,不是清除后的新状态。
oldState = atomic.AndUint64(state, keepMask)
// 只从旧快照判断目标位,避免并发更新改变判断依据。
wasSet = oldState&bitsToClear != 0
return oldState, wasSet
}
例如 bitsToClear 同时包含在线位和错误位时,两个目标位都会被清零,其余 62 位保持原值。wasSet 只说明清除前至少有一个目标位为 1,不代表清除后状态字仍然包含它们。

把位清除封装成稳定的状态接口
业务代码不要到处手写取反表达式,可以把位定义和清除动作放在同一个类型附近。这样调用方只传“想撤销的状态”,不容易误把保留掩码传错。
type StateWord uint64
func (s *StateWord) Clear(bits uint64) (old StateWord) {
// bits 表示调用方希望清除的位,方法内部负责转换为保留掩码。
old = StateWord(atomic.AndUint64((*uint64)(s), ^bits))
return old
}
func (s StateWord) Has(bits uint64) bool {
// 读取快照时使用值接收者;复合状态判断仍需由更高层设计同步边界。
return uint64(s)&bits != 0
}
这个封装解决的是位掩码表达的一致性,不会自动把多个普通字段读写变成事务,也不会替代锁。若业务需要“检查多个条件后再决定是否更新”,应重新审视整个状态转换是否需要 CAS 或互斥保护。
64 位对齐和 atomic.Uint64 的选择
官方文档提醒,在 ARM、386 和 32 位 MIPS 等平台上,通过旧式原子函数访问 64 位字时,调用方需要负责安排 64 位对齐;把 uint64 放在结构体中时尤其要留意布局。使用类型封装通常更清楚:
type State struct {
// atomic.Uint64 自带适合原子访问的类型语义,且不能在首次使用后复制。
flags atomic.Uint64
}
func (s *State) Clear(bits uint64) (old uint64) {
// Uint64.And 与 AndUint64 的掩码规则相同:传入要保留的位。
return s.flags.And(^bits)
}
atomic.Uint64.And 适合新代码,因为状态字段本身表达了“这是原子 uint64”,也减少了手动取地址和布局误用的机会。若需要兼容既有的 *uint64 API,atomic.AndUint64 仍然可以使用,但应把对齐和复制规则写进类型约定。
常见误区和速查表
| 场景 | 正确做法 | 原因 |
|---|---|---|
| 清除某些位 | AndUint64(addr, ^bitsToClear) | 目标位与 0 做 AND,其他位与 1 做 AND |
| 判断清除前是否开启 | 检查返回的 oldState&bitsToClear | 返回值是原子更新前的快照 |
| 新增原子状态字段 | 优先考虑 atomic.Uint64 | 类型语义和对齐边界更明确 |
| 复合条件更新 | 评估 CAS 或锁 | 一次 AND 只保证这次位更新原子 |
相关问题
直接传 bitsToClear 会怎样?
会把待清除位之外的大多数位清零,因为按位 AND 的 0 会清除对应位置。清除状态位时必须传反掩码。
返回值是更新后的状态吗?
不是。AndUint64 返回的是更新前的旧值;如果需要新值,应根据已知掩码推导,或在明确的同步边界下再次原子读取。
AndUint64 能替代所有锁吗?
不能。它只适合单个 64 位状态字的原子按位更新;跨字段不变量、复合检查和多步状态迁移仍需要更完整的同步方案。
记住一句话:要清除哪些位,就把这些位取反后交给 AND;要判断清除前的状态,就读取函数返回的旧值。新代码可用 atomic.Uint64.And 表达同样语义,并把原子字段的类型边界写得更清楚。
-
339 收藏
-
185 收藏
-
333 收藏
-
210 收藏
-
318 收藏
-
132 收藏
-
289 收藏
-
488 收藏
-
243 收藏
-
342 收藏
-
450 收藏
-
398 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习