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 的非负整数,超限会截断典型并发场景如「仅允许第一个 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() 不提供任何并发保护能力,它只是原子写,不解决“谁该写”的问题cmpset() 无法一步完成,得靠循环重试或换用 Table + 行锁复杂点不在语法,而在你是否清楚自己要解决的是“状态一致性”还是“值覆盖”。用错一个,轻则逻辑重复,重则状态错乱,且难以复现。