一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Swoole中SwooleAtomic::cmpset和set的区别

时间:2026-07-21 09:35:49 编辑:袖梨 来源:一聚教程网

cmpset是条件写、set是无条件覆盖;cmpset仅当当前值等于预期值时才写入新值并返回操作后值,需严格比较返回值;set直接覆盖,无返回值,适合初始化或重置。

cmpset 是条件写,set 是无条件覆盖

SwooleAtomic::cmpset() 不是赋值函数,它只在当前值等于预期值时才写入新值;set() 则不管当前值是什么,直接覆盖。这是根本区别,决定了它们的使用边界。

常见错误是把 cmpset() 当成“带判断的 set”来用,比如想先检查是否为 0 再设为 1,却写成:if ($atomic->get() === 0) { $atomic->set(1); }——这中间存在竞态窗口,两个进程可能同时通过 get() 判断,然后都执行 set(1),导致逻辑失控。

  • cmpset($cmp_value, $new_value) 返回的是操作后的当前值(不是布尔值),必须显式比较:if ($atomic->cmpset(0, 1) === 1) { /* 成功 */ }
  • set($value) 没有返回值,调用即生效,适合初始化、重置等无需条件判断的场景
  • 参数范围一致:两者都要求 $value 是 ≤ 4294967295 的非负整数,超限会截断

cmpset 天然用于并发竞争,set 适合单次设定

典型并发场景如「仅允许第一个 Worker 完成配置加载」或「告警开关只触发一次」,必须用 cmpset()。它底层依赖 CPU 的 cmpxchg 指令,整个“比-换”动作不可分割,多个进程同时调用,只有一个能成功。

set() 没有竞争语义,适合服务启动时一次性设定初始状态,或定时任务中强制覆盖某个监控指标(如每分钟重置计数器)。

  • 若需“首次初始化”,不要用 set() + 外层 if,而应只调用一次 cmpset(0, 1) 并检查返回值
  • 若需“总是设为某值”,比如清零计数器,set(0) 比反复 cmpset($cur, 0) 更直接、无失败路径
  • 混用时注意:先 set()cmpset() 没问题;但 cmpset() 失败后直接 set() 可能掩盖本该拒绝的并发冲突

性能与可见性没有本质差异,但语义误用代价更高

两者底层都走共享内存+CPU 原子指令,单次调用开销接近。但 cmpset() 的失败重试逻辑(如有)会带来实际性能波动,而 set() 是稳定低开销。

真正容易被忽略的是语义层面:很多人以为 cmpset() 返回 true/false,结果写出 if ($atomic->cmpset(0, 1)) { ... } ——这在 PHP 8+ 中可能因返回整数被隐式转为 true 而“看似正常”,但一旦 $new_value 是 0,就永远进不去分支。

  • 永远用严格比较:=== $new_value,不要依赖真假值转换
  • set() 不提供任何并发保护能力,它只是原子写,不解决“谁该写”的问题
  • 如果业务需要读-改-写复合操作(比如“加 1 但不超过 100”),cmpset() 无法一步完成,得靠循环重试或换用 Table + 行锁

复杂点不在语法,而在你是否清楚自己要解决的是“状态一致性”还是“值覆盖”。用错一个,轻则逻辑重复,重则状态错乱,且难以复现。

热门栏目