何时可拆分原子Read-Modify-Write为松弛操作+屏障?CL-Deque实现分析
CL-Deque中拆分bottom原子递减的合法性分析
一、拆分操作合法的核心原因
CL-Deque的设计有个关键约束:只有队列的所有者线程会执行take操作(修改bottom指针),所有窃取线程仅读取bottom,不会对其执行写入操作。
基于这个约束,拆分操作的逻辑完全安全:
- 以
relaxed序加载bottom并本地减1:因为没有其他线程写bottom,不存在读取到中间值的风险;relaxed序的内存开销最小,不需要同步其他内存操作。 - 以
relaxed序存储新的bottom值:同样,单线程写入不会有竞态写问题,relaxed序足够。 - 最后添加
seq_cst屏障:目的是保证bottom的修改与后续队列元素的访问、以及窃取线程的读操作之间建立全局同步关系,确保窃取线程看到的bottom和top值的相对顺序是正确的,避免出现top > bottom的无效判断。
二、并发窃取线程是否会看到不同的bottom值?
会,但这完全符合CL-Deque的设计预期,不会导致错误:
- 窃取线程读取
bottom时,由于relaxed序的特性,可能看到滞后的旧值,但CL-Deque的steal逻辑本身带有CAS校验:窃取时会先读取top和bottom,若top >= bottom则判定队列为空;否则尝试通过CAS将top加1,只有CAS成功时才会取出元素。 - 即使窃取线程看到了旧的
bottom值,后续的CAS操作会自动过滤掉无效的窃取尝试,保证数据一致性。
三、与atomic_fetch_sub方案的对比
atomic_fetch_sub是原子性的递减操作(默认内存序为seq_cst),确实能消除所有潜在竞态,但在CL-Deque的场景下完全没必要:
- 拆分方案利用了「单线程写bottom」的约束,用更廉价的
relaxed操作替代了原子递减,仅用一个seq_cst屏障保证必要同步,整体性能开销更低,适合高频率take的场景。 - 原子递减操作的开销更高,因为它需要硬件层面的总线锁定或缓存一致性协议干预,而拆分操作几乎没有这些额外开销。
四、是否属于通用技术?
不算通用技术,它严重依赖CL-Deque的特定设计前提:某个原子变量仅由单个线程写入,其他线程仅读取。
- 如果换成多线程均可写入该变量的场景,这种拆分操作会直接导致竞态写问题(多个线程同时加载、减1、存储,最终丢失更新),完全不安全。
- 只有在满足「单线程写、多线程读」的原子变量场景下,才能用这种拆分方式优化性能,不具备通用性。
内容的提问来源于stack exchange,提问作者Sasha Krassovsky
相关产品推荐
相关产品推荐

