繁体   English   中英

什么时候可以将原子读-修改-写操作分解为组成松散操作+障碍?

[英]When is ok to break up atomic Read-Modify-Write operations into constituent relaxed operations + barrier?

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 操作结束。

这是原始文件

所有宽松操作之后的障碍不会使它们像seq_cst操作; 它不会有发布语义。 较早的加载/存储,因为没有将它们分开的障碍。 如果在那之后有一个更relaxed的商店,你可以将它seq_cst ,或者如果你不确定所订购的所有东西,请保留单独的围栏; 围栏比操作更坚固。


当您是唯一可以同时写入此变量的线程时,分解++-- (或其他 RMW)是安全的。 (曾经或现在,因为我们持有锁或以其他方式拥有独占所有权)。 例如在 SeqLock 或单一生产者队列中。

读者很好,这就是您使用std::atomic的原因。 但根据定义,读者不能踩到我们的 atomic-load/modify/atomic-store 序列。 因此我们不需要 RMW 原子性,只需要存储本身的原子性,这是读者线程能够看到的全部。 无论是 RMW 的商店端还是纯粹的商店都没有关系。

如果不是这种情况,您以后将无法“修复”它; 损害可能已经造成。 但是,您可以设计一个完整的算法,以便如果之前出现问题,可以在以后检测到它,然后您就可以摆脱困境。 那可能就是你在说什么,但是 IDK,我没有调查你链接的代码的细节,只是想回答一般问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM