【发布时间】:2014-10-01 17:13:02
【问题描述】:
bool compare_exchange_weak (T& expected, T val, ..);
compare_exchange_weak() 是 C++11 中提供的比较交换原语之一。它是weak,即使对象的值等于expected,它也会返回false。这是由于虚假失败在某些平台上使用一系列指令(而不是 x86 上的一个)来实现它。在这样的平台上,上下文切换、另一个线程重新加载相同地址(或缓存行)等可能会使原语失败。这是spurious,因为它不是使操作失败的对象的值(不等于expected)。相反,这是一种时间问题。
但令我困惑的是 C++11 标准 (ISO/IEC 14882) 中所说的话,
29.6.5 .. 虚假失败的一个后果是几乎所有使用弱 比较和交换将处于循环中。
为什么它必须在几乎所有用途中循环?这是否意味着当它由于虚假故障而失败时我们将循环?如果是这样,我们为什么还要麻烦使用compare_exchange_weak() 并自己编写循环呢?我们可以只使用compare_exchange_strong(),我认为它应该为我们消除虚假的失败。 compare_exchange_weak()的常见用例有哪些?
另一个相关的问题。 Anthony 在他的《C++ Concurrency In Action》一书中说,
//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) 也提到了这一点:
当比较和交换处于循环中时,弱版本将产生 在某些平台上表现更好。
但如上所述,循环中的两个版本应该提供相同/相似的性能。我错过了什么?
【问题讨论】:
-
W/r/t 第一个问题,在很多情况下你无论如何都需要循环(无论你使用强版本还是弱版本),弱版本可能比强版本有更好的性能。
-
弱CAS和强CAS都是“使用LL/SC”实现的,就像冒泡排序和快速排序都是“使用swap”实现的一样;也就是说,从某种意义上说,这是用于完成任务的原始操作。他们围绕 LL/SC 的内容非常不同。弱 CAS 只是 LL/SC。强 CAS 是 LL/SC 和一堆其他东西。
-
@TuXiaomi 与该链接中的答案,我不明白为什么“弱版本会在某些平台上产生更好的性能”如标准中所述。
-
@Deqing 在其他情况下,compare_exchange_weak 可能由于其他处理器或线程的中断或操作而虚假失败。在这些平台上, compare_exchange_strong 实际上是 compare_exchange_weak 上的一个循环——如果它虚假地失败,那么它会再次循环。它有帮助吗?也许我错了
标签: c++ multithreading c++11 atomic