拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 比较和交换回圈如何实作原子性?

比较和交换回圈如何实作原子性?

白鹭 - 2022-02-17 2072 0 0

本页详细讨论了 CAS 回圈:https : //preshing.com/20150402/you-can-do-any-kind-of-atomic-read-modify-write-operation/

fetch_multiplyC 中的一个示例

uint32_t fetch_multiply(std::atomic<uint32_t>& shared, uint32_t multiplier){
    uint32_t oldValue = shared.load();
    while (!shared.compare_exchange_weak(oldValue, oldValue * multiplier)){}
    return oldValue;
}

本质上,如果*memory值与我们的 oldValue 匹配,则 anewValue会自动存盘,否则 oldValue 会更新为*memory.

我有两个问题:

1 - 为什么我们必须检查oldValue存储器中是否仍然没有变化?如果我们只是将 newValue 写入存储器会发生什么?我们是否试图避免覆写或使用来自另一个执行绪的中间值?

2-假设这个场景有 2 个执行绪:

  • 执行绪 B 试图以非原子方式存盘未对齐的值。发生存盘撕裂。
  • 执行绪 A 尝试交换。
  • 交换失败,因为 oldValue 不匹配。存储器中中间(撕裂)值被加载到我们的 oldValue。
  • 执行绪 A 与一个中间相乘并尝试另一个成功的交换。
  • 现在执行绪 B 将其剩余的值写入相同的位置,部分覆写我们之前的写入。

我假设Thread B可以以这幺多的延迟运行,如果是这样,我们不仅乘以一个中间值,它甚至还被部分覆写,而 CAS 什么也没做。

uj5u.com热心网友回复:

我设法说服自己代码是错误的我认为它应该是这样的:

uint32_t fetch_multiply(std::atomic<uint32_t>& shared, uint32_t multiplier){
    uint32_t oldValue;
    do {
        oldValue = shared.load();
    } while (!shared.compare_exchange_weak(oldValue, oldValue * multiplier));
    return oldValue;
}

如果目标值更改为其他值,我们需要再次读取 oldValue ,否则我们将永远旋转。

但是,CAS 构造的要点是您永远无法在共享位置观察到中间值。眼泪是不可能的;shared.load()防止它。这是在硬件中实作的。

“如果我们只是将 newValue 写入存储器会发生什么?” 那么你没有原子访问权限。始终遵循模式。

“非对齐值”如果shared是非对齐的,您甚至在谈论std::atomic. 非对齐指标不能安全地取消参考。对于正常情况,*您只是依赖于字节可寻址架构,但这是一个std::atomic. 如果它没有对齐,即使在 x86 上也可能出现故障。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *