【问题标题】:What is Compare And Swap good for?比较和交换有什么用?
【发布时间】:2012-05-04 20:56:16
【问题描述】:

我最近在阅读有关 Compare And Swap 原子操作(CMPXCHG、.NET 的 Interlocked.CompareExchange 等)的信息。

我了解它在内部是如何工作的,以及它是如何从客户那里使用的。

我不太清楚的是什么时候有人会使用 CAS?

维基百科说:

CAS 用于实现同步原语,例如 信号量和互斥体,同样更复杂的无锁和 免等待算法。

那么,谁能给我一个更通用的真实用例,其中包含 CAS 使用的代码和描述?

此问题与语言无关,因此任何语言都可以(首选基于 C 或 x86 汇编)。

谢谢!

【问题讨论】:

    标签: c# c assembly thread-safety compare-and-swap


    【解决方案1】:

    通过示例很容易看出这一点。假设我们想在共享变量上原子地并发设置一些位:

    int shared = 0;
    
    void Set(int index) {
     while (true) {
      if (Interlocked.CompareExchange<int>(ref shared, shared | (1 << index), shared) == shared)
       break; //success
     }
    }
    

    如果我们看到“旧值”(即返回值)在此期间发生了变化,我们就会检测到失败。

    如果这没有发生,我们没有并发修改,所以我们自己的修改成功。

    您可以使用这种技术实现相当复杂的东西。不过,越复杂,旋转造成的性能损失就越大。

    我想强调的是,CAS 的一个关键特性是它可以失败并且可以可靠地检测到失败。

    【讨论】:

    • 谢谢!你能澄清一下“你可以实现相当复杂的东西..”吗?什么样的东西?
    • 我想你可以用这个实现任意函数“F(shared)”。在我的示例中,F(shared) 是“shared | (1
    • 请注意,如果您不打算返回旧值,则执行 Interlocked Or(在 x86-64 上,单个 lock or 指令)而不是 CAS 重试更有效环形。当语言可以以更简单的方式表达它们时,最好不要从 CAS 合成它们,这可以是某些机器上的单个原子 RMW 指令。 x86 没有fetch_or(尽管它确实有通过lock xadd 的fetch_add),但对于单个位,它确实有lock bts 可以在内存中原子地设置一个位并记录旧值。 (位测试和设置)
    • 我可能选择了左移或清除最低设置位 (x &amp;= x-1) 作为 CAS 重试循环的演示;使用不同的互锁操作无法完成的事情。或者作为实现 atomic float 的一部分,如果 C# 不能为您做到这一点。
    【解决方案2】:

    您使用 CAS 在一个线程或进程中原子地设置一个值(一个位或一个字),同时测试另一个线程/进程尚未这样做。所以它用于在多线程环境中获取一个标志或计数器。

    【讨论】:

    • 那时我们只有一个核心。现在线程可以跨越多个内核,那么这如何应用呢? (我可能遗漏了什么)
    • @kubal5003 - 这尤其适用于多核,因为它保证了对单词的原子(单 CPU/线程/核)访问。
    • 对于单个布尔标志,包含字节上的 xchg (Interlocked.Exchange) 也差不多。要获得锁,您可以换入1,然后查看旧值是否为0。 (就像这个x86-64 asm spinlock toy example)。不是 CAS 真正证明其价值与更简单事物的用例。它对于计数锁/信号量具有更多价值,您不想只使用 fetch_add (x86 lock xadd) 和 -1,因为这可能会使计数器低于 0。
    【解决方案3】:

    那么,谁能给我一个更通用的真实用例,其中包含 CAS 使用的代码和描述?

    This论文使用CAS实现无锁线程安全队列。

    里面有一些伪代码示例。

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    • 欢迎来到 Stack Overflow!请通过详细说明或解释链接的内容来改进您的答案。它应该能够独立存在,外部文档或资源仅用作进一步阅读的补充。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-10
    • 2011-05-11
    • 2013-10-27
    • 2021-03-09
    相关资源
    最近更新 更多