登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go atomic.Uint32 And 怎样按位清除标志并拿到旧值

来源:17golang原创

时间:2026-10-28 17:45:07 111浏览 收藏

Go atomic.Uint32 And 怎样按位清除标志并拿到旧值

如果一个并发状态由多个 bit 组成,想清除其中一个标志,atomic.Uint32.And 的关键写法不是传入这个标志本身,而是传入它的按位补码:state.And(^flag)。And 会原子地把当前值与掩码相与,并返回更新前的旧值,所以既能完成清除,也能知道这次操作前该位是否存在。

要清除某一位,就保留其他位、把目标位置零;因此使用 ^flag,而不是直接使用 flag。

三个记忆点:And 的参数是“保留掩码”;返回值是“修改前的整个位图”;原子性只覆盖这个 uint32,不自动保护旁边的业务字段。

先把 And 看成按位更新

我在整理状态位时最容易犯的错,是把“要清除的位”直接传给 And。假设当前状态是 111,要清除中间的 010,正确结果应为 101。因此掩码要写成 101,也就是目标 flag 的按位补码。

用位运算表达就是 新值 = 旧值 & 掩码。对于只占一位的 flagDirty,^flagDirty 会让 DIRTY 位变成 0,其余 31 位保持 1。这样既不会误清除其他标志,也不需要先 Load 再 Store。

atomic.Uint32.And 的状态位、保留掩码与旧值关系说明图
图1:按位清除的静态结构说明图,查看状态字、补码掩码和旧值返回关系;这不是运行截图。

用 ^mask 清除指定标志

下面把 READY、DIRTY、ERROR 三个状态放进同一个 atomic.Uint32。示例只清除 DIRTY,其他两个标志仍然保留。代码中的注释说明了掩码、返回值和读取顺序。

package main

import (
    "fmt"
    "sync/atomic"
)

const (
    flagReady uint32 = 1 

这里旧值是 111,更新后的值是 101。如果业务只关心“清除前是否置位”,可以检查 old&flagDirty != 0;如果还要判断其他状态,则保留完整的旧位图,而不是只保留一个 bool。

利用返回值处理组合状态

And 返回的是整个位图,不是被清除的那一位。这个设计在消费状态、记录变更日志或判断是否需要触发后续动作时很有用:调用前先拿到的旧值与清除动作属于同一次原子更新,不需要用“先 Load、再判断、再 Store”的三步组合。

func clearDirty(state *atomic.Uint32) bool {
    // 先清除 DIRTY,再从同一次 And 的旧值判断它是否原来存在。
    old := state.And(^flagDirty)
    return old&flagDirty != 0
}

func hasError(state *atomic.Uint32) bool {
    // Load 只读取当前快照,不改变任何标志位。
    return state.Load()&flagError != 0
}

要注意,^flagDirty 是针对整个 uint32 生成的补码。如果把多个标志一起清除,可以写成 state.And(^(flagDirty | flagError));如果只想清除其中一个,就不要把其他 flag 合并进目标集合。

And 返回旧位图、Load 读取新位图及组合标志边界说明图
图2:旧位图与新位图的关系说明图,展示 And 返回旧状态、Load 读取当前状态的静态边界。

划清并发边界

atomic.Uint32.And 适合维护一个不超过 32 位的独立状态字。它保证这次按位更新不会被另一个对同一原子值的操作拆开观察,但不会让与状态相关的普通结构体字段自动变成原子访问。

  • 零值可用:atomic.Uint32 的零值表示数值 0,可以直接调用 Load、Store 或 And。
  • 不要复制:第一次使用后不要复制 atomic.Uint32;把它放进结构体时应通过指针或稳定的结构体实例使用。
  • 确认版本:Uint32.And 是较新的按位原子操作,项目需要确认编译环境支持该方法;不支持时不能只改写调用名称。
  • 别把 And 当 CAS:And 无法表达“只有当前值等于 old 才更新”的条件状态机;那是 CompareAndSwap 的问题边界。

相关问题

为什么不能写成 state.And(flagDirty)?

因为这样会保留 DIRTY 位、清除其他位,语义与“清除 DIRTY”相反。清除目标位时要传入目标位的补码。

And 的返回值是清除后的值吗?

不是。它返回更新前的旧值;需要当前位图时再调用 Load,或者直接使用后续业务读取到的快照。

多个 goroutine 同时清除不同标志安全吗?

对同一个 atomic.Uint32 使用 And 是原子的,适合并发清除位;但关联数据的读写仍需单独设计同步关系。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>