“RDCSS常作为多字CAS的内部步骤:只有大操作status仍Undecided时,才把它的描述符安装到某个数据位置。它只负责一个数据位置的条件占位;对多个地址的统一成功/失败,还需要上层单…”
形式陈述
多字比较并交换CASN接受k≥1个不同地址及旧、新值三元组
若在一个抽象原子时刻所有地址都含相应旧值,就同时把它们改为新值并返回true;否则不改变这些逻辑值并返回false。它不是分别对各字作CAS后,希望恰好没有人看见中间态。
本页采用SC、单字CAS与RDCSS。所有参与者按同一个全序访问地址,例如按地址编号递增。用户数据与两层描述符标记互不混淆;描述符新鲜、字段发布后不变,在迟到帮助者可能访问期间不回收。受CASN管理的数据地址只通过CASN及CASNRead访问,不混入忽略协议的裸写。
大描述符d含排序后的三元组和原子状态status∈{U,S,F},分别表示未决、成功、失败。其核心帮助过程为:
help(d):
if d.status == U:
proposed = S
for (a,old,new) in d.entries, ascending address order:
retryEntry:
r = rdcss(fresh descriptor(d.status,U,a,old,C(d)))
if r is C(other) and other != d:
help(other)
goto retryEntry
if r != old and r != C(d):
proposed = F
break
CAS(d.status, U, proposed)
succeeded = (d.status == S)
for (a,old,new) in d.entries:
CAS(a, C(d), new if succeeded else old)
return succeeded
C(d)是CASN描述符标记,与RDCSS的临时标记D(r)不同。CASNRead(a)先调用rdcssRead,若得到C(d)就help(d)并重读,直到取得普通数据值;用户不会直接看到内部描述符。
直觉
第一阶段把各地址换成同一张事务式工作单,却暂时仍把它们解释为旧值。一个status决定这张单据代表“全部用新值”还是“全部保留旧值”。第二阶段再把单据逐处换回普通数值,只做表示清理。
这里的关键是把逻辑值变化集中到一次状态决断,而把物理清理分散到许多单字CAS。公开工作单使遇到冲突的线程可以帮助,不需要等待原发起者释放一把锁。
例子与边界
把(1,2)原子交换成(2,1)
初始x=1、y=2,且x<y。描述符d要求 (x,1,2),(y,2,1)。
| 阶段 | 物理x | 物理y | d.status | 逻辑(x,y) |
|---|---|---|---|---|
| 初始 | 1 | 2 | U | (1,2) |
| 占住x | C(d) | 2 | U | (1,2) |
| 占住y | C(d) | C(d) | U | (1,2) |
| 状态决断 | C(d) | C(d) | S | (2,1) |
| 清理x | 2 | C(d) | S | (2,1) |
| 清理y | 2 | 1 | S | (2,1) |
统一解释规则为:普通字表示自身;指向未决或失败描述符的字表示该项old;指向成功描述符的字表示该项new。RDCSS临时占位先按下层协议处理。
如果T1占住x后暂停,T2在x处遇到C(d),可以替它占住y、决断并清理。T1恢复时发现状态已经是S,清理CAS即使失败也不表示大操作失败,因为其他人可能早已完成清理。
失败点早于失败状态写入
仍要求旧对(1,2),但实际初始为(1,8)。d可先占住x,随后在y读到8,提出F,最终把status从U改为F并把x恢复1。
成功CASN的线性化点是status由U变S的CAS:此前全部地址已经归本描述符代表,逻辑值同时从old切到new。失败CASN则可放在导致最终失败决定的那次不匹配普通值读取,此处至少一个比较确实不满足。
不能一律把失败放在U→F处。一个帮助者可能先读到y=8后暂停,另一个操作把y改回2,再由前者写F;到写F时全部期待值可能已重新成立,但此前读8的时刻仍是合法失败见证。失败决定本身只固定结果,不把旧观察改写成当前观察。
两个相交操作怎样帮助
设d先占x;另一个e也需要x和y,且它的期待值为d成功后的(2,1)。e在处理x时遇到C(d),先help(d)。d完成后,e重试并读到x=2,再占x、检查y=1,最后可成功完成自己的更新。
若e仍期待旧(1,2),帮完d后便在x读到2,失败。这是合法的串行次序d先、e后,不是帮助把e的期待值偷偷改掉。
若不统一获取顺序,d可能先占x再等y,e先占y再等x,递归帮助会形成环。全局递增顺序使未决操作在共同地址上的依赖沿更高的已占位置推进,排除这种循环占位结构;它是无锁进展论证的一部分,不只是减少冲突的建议。
推论与应用
三个不变量支撑算法:status只能U→S或U→F一次;未决阶段的占位保留逻辑旧值;终态后的清理CAS只写该状态已经代表的值。多个帮助者可以提出相同或不同局部判断,但只有首次成功的状态CAS决定最终结果,后来者必须重新读取该状态再清理。
RDCSS不可随意换成“先读status,再普通CAS数据字”。迟到帮助者可能在大操作已经决断并清理后,把旧工作单再次装进数据位置。RDCSS把“仍未决”与占位合成下层原子动作,阻止这种过期占位在上层逻辑中重新生效。
CASN的原子更新不等于自动提供多字读取快照。一个线程先CASNRead(x),大更新发生,再CASNRead(y),仍可能得到来自两个不同时刻的值;每次单字读都合法。需要原子快照时,应使用另外的读协议,不能仅因写者使用CASN就省略它。
无竞争、地址已排序且分配视为常数成本时,宽度k的占位和清理做
描述符回收、内存分配与弱内存发布另有成本和证明责任。一个CASN已返回,不代表所有迟到线程都不再持有它的地址;只能在安全生命周期协议允许后复用身份。本文的SC、新鲜描述符模型把这些责任明确隔离,并未将它们视作自动解决。
参考资料
- Timothy L. Harris, Keir Fraser, Ian A. Pratt, “A Practical Multi-Word Compare-and-Swap Operation”, DISC, 2002,§5、Figure2;§5.1 Lemmas4–5分别给失败与成功线性化点;§6讨论描述符实现责任