使用 Load-link/store-conditional 来防止 ABA 的无锁 C++11 示例?

Posted

技术标签:

【中文标题】使用 Load-link/store-conditional 来防止 ABA 的无锁 C++11 示例?【英文标题】:Lock-free C++11 example using Load-link/store-conditional to prevent ABA? 【发布时间】:2014-05-30 13:08:32 【问题描述】:

使用比较和交换 (CAS) 技术编写无锁代码时,会出现一个称为 ABA 问题的问题:

http://en.wikipedia.org/wiki/ABA_problem

只比较值“A”是有问题的,因为在两个观察之间仍然可能发生写入。我继续阅读并找到了这个解决方案:

http://en.wikipedia.org/wiki/LL/SC

在计算机科学中,加载链接和条件存储 (LL/SC) 是 多线程中使用的一对指令来实现 同步。 load-link 返回内存的当前值 位置,而随后的存储条件是相同的内存 只有在没有发生更新时,位置才会存储新值 自加载链接以来的那个位置。总之,这实现了一个 无锁原子读-修改-写操作。

如何修改典型的 C++ 无锁 CAS 技术以使用上述解决方案?有人可以举一个小例子吗?

我不介意它是否特定于 C++11/x86、x86-64 Linux(最好没有 Win32 答案)。

【问题讨论】:

【参考方案1】:

LL/SC 是由某些架构(例如 SPARC)实现的指令,用于形成更高级别的原子操作的基础。在 x86 中,您可以使用 LOCK 前缀来实现类似的目标。

为了避免 x86 上 LOCK 的 ABA 问题,您必须提供自己的保护以防止干预存储。一种方法是在相关内存附近存储一个代号(只是一个递增的整数)。每个更新程序都会进行足够广泛的原子比较/交换,以包含数据和序列号。只有找到正确的数据和正确的数字,更新才会成功。同时,它会更新数字,以便其他线程看到变化。

您会注意到,x86 一直 (?) 提供了一个 CMPXCHG 指令,其宽度是机器字的两倍(请参阅 CMPXCHG8B 和后来的 CMPXCGH16B),可用于此目的。

【讨论】:

你是说CMPXCHG实现了CAS2/Double CAS? CMPXCGH8B(在 32 位系统上)或 CMPXCHG16B(在 64 位系统上)更像是“双 width CAS”。一般的 CAS2 可以在非连续内存上工作。 假设您在指针上使用 CAS,请记住,在 64 位的 Windows 上,8 字节对齐的地址中有 23 个未使用的位。我只是使用一个联合来封装一个 64 位数字,以及一个包含我的 ptr/aba 成员的结构。在 linux 中,除非您想处理完整的 64 位范围,否则您可以使用相同的技巧。我的经验是 128 位的 CAS 比 64 位的要慢,所以我总是尽量塞进 64 位。 如果你有指针的 ABA 问题,那么你也有生命周期管理问题。至少我不知道有任何方法可以解决指针的 ABA 问题,而在某些时候没有持有悬空指针。 Herb Sutter 对此进行了演讲,他提出的解决方案是在 MSVC for C++20 中实现的,希望有一天它会在所有地方实现:youtu.be/CmxkPChOcvw

以上是关于使用 Load-link/store-conditional 来防止 ABA 的无锁 C++11 示例?的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)