bool compare_exchange_weak (T& expected, T val, ..);
compare_exchange_weak()
是 C++11 中提供的比较交换原语之一。它在某种意义上是弱的,即使对象的值等于expected
,它也会返回false。这是因为在某些平台上(而不是x86平台上的单个指令),使用一系列指令来实现它可能会导致虚假失败。在这些平台上,上下文切换、另一个线程重新加载相同地址(或缓存行)等操作可能会导致原语失败。这种失败是虚假
的,因为导致操作失败的不是对象的值(与expected
不相等),而是一种时间上的问题。
但是令我困惑的是C++11标准(ISO/IEC 14882)中的一段话:
为什么几乎所有的用法都要使用循环呢?这是否意味着当由于虚假失败而失败时,我们应该进行循环?如果是这样的话,为什么我们还要使用compare_exchange_weak()并自己编写循环呢?我们可以直接使用compare_exchange_strong(),我认为这样可以消除虚假失败。compare_exchange_weak()的常见用例有哪些?29.6.5 .. 虚假失败的一个结果是,几乎所有使用弱比较交换的情况都会在循环中。
另一个相关的问题。在他的书《C++并发编程实战》中,Anthony说,
//Because compare_exchange_weak() can fail spuriously, it must typically
//be used in a loop:
bool expected=false;
extern atomic<bool> b; // set somewhere else
while(!b.compare_exchange_weak(expected,true) && !expected);
//In this case, you keep looping as long as expected is still false,
//indicating that the compare_exchange_weak() call failed spuriously.
为什么在循环条件中有
!expected
?它是为了防止所有线程可能会饿死并在一段时间内没有进展吗?最后一个问题:
在没有单个硬件CAS指令的平台上,弱版本和强版本都使用LL/SC(如ARM,PowerPC等)来实现。那么下面两个循环有什么区别吗?如果有的话,为什么?(对我来说,它们应该具有类似的性能。)
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_weak(..))
{ .. }
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_strong(..))
{ .. }
我提出了最后一个问题,你们都提到在循环中可能会有性能差异。这也在C++11标准(ISO/IEC 14882)中提到过:
当一个比较和交换操作在循环中时,弱版本在某些平台上会有更好的性能。
但是根据上面的分析,循环中的两个版本应该具有相同/相似的性能。我错过了什么?