c - 什么时候可以将原子读取-修改-写入操作分解为组成宽松的操作+屏障?

标签 c multithreading c++11 atomic memory-model

model-checked implementation of the CL-Deque ,他们使用以下策略来减少 bottom 指针:

size_t b = atomic_load_explicit(&q->bottom, memory_order_relaxed) - 1;
Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed);
atomic_store_explicit(&q->bottom, b, memory_order_relaxed);
atomic_thread_fence(memory_order_seq_cst);

因此,他们加载 bottom 指针,在本地递减它,然后存储它。为什么这样做是有效的?并发窃贼是否无法看到任一 bottom 值?

执行此操作的另一种方法是将读取-修改-写入操作组合到单个atomic_fetch_sub中,如下所示:

Array *a = (Array *) atomic_load_explicit(&q->array, memory_order_relaxed);
size_t b = atomic_fetch_sub_explicit(&q->bottom, 1, memory_order_seq_cst) - 1;

这将消除可能的竞争条件。

认为分解版本是有效的,因为stealtake函数内部的CAS稍后解决了这个竞争,仅当双端队列足够小以至于它很重要时。但这是通用技术吗?

最佳答案

这是一个单生产者/多消费者队列,其中生产者使用推送/获取操作,消费者使用“窃取”操作。生产者是唯一修改“底部”变量的人。因此,使用原子 RMW fetch_sub 是多余的。无论如何,消费者(小偷)将在存储/子操作之前或之后看到该值。重要的方面是事务语义,正如您所指出的,事务语义是通过两端的 CAS 操作得出结论的。

这是original paper .

关于c - 什么时候可以将原子读取-修改-写入操作分解为组成宽松的操作+屏障?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74838399/

相关文章:

c - 如何从 loadrunner vugen 脚本运行 linux 命令?

c# - 调度任务以供将来执行

ruby-on-rails - 如果 Puma 是多线程而不是多进程,为什么要创建多个 PID?

C++ 减少冗余

c++ - 你能使用 constexpr 变量的地址吗?

c - 如何在c中调用以结构体作为参数的函数

c - 使用指针访问数组

c - 如何检测在 C 中按下 shift 键?

c# - 使用 await 和 async 在 long 方法运行时释放 UI 线程

c++ - 使用线程类在C++中构造线程的动态数组时出错