这两个函数都属于 原子操作的cas操作也就是Compare-and-swap操作,经常在无锁并行或并发结构中用到。以compare_exchange_strong为例子来做个基本的使用说明:
int tst_val = 4;
int new_val = 5;
std::atomic atomic_0 = 3;
auto exchanged = atomic_0.compare_exchange_strong(tst_val, new_val);
/*
上面这句代码执行后, 各个相关变量中的值如下:
atomic_0 = 3, tst_val = 3, new_val = 5, exchanged = false
由于atomic_0 和 tst_val两个变量实际内存中的值不相等,所以atomic_0的值依旧是3并没有被设置为new_val的值5, atomic_0的值依旧是3。atomic_0.compare_exchange_strong(...)函数的返回值是false。但是,这句代码执行之后,tst_val的内存值被设置为atomic_0原子变量的内存值3。相当于执行了把atomic_0变量内存中的值载入到tst_val变量中的载入操作。
*/
auto exchanged = atomic_0.compare_exchange_strong(tst_val, new_val);
/*
上面这句代码执行后, 各个相关变量中的值如下:
atomic_0 = 5, tst_val = 3, new_val = 5, exchanged = true
由于atomic_0 和 tst_val两个变量实际内存中的值相等,所以atomic_0的值被设置为new_val的值5。atomic_0.compare_exchange_strong(...)函数的返回值是true。注意,这句代码执行之后,tst_val的内存值没有变。相当于执行了将new_val内存中的值存储到atomic_0变量内存中的设置操作。
*/
compare_exchange_weak和compare_exchange_strong用法一样,只不过由于在某些弱内存顺序的CPU架构下compare_exchange_weak执行会正常反馈出伪失败。也正因为如此,实际不考虑特别的性能以及考虑到兼容性,用compare_exchange_weak就行了。反正一般的无锁并行或者并发数据结构实现中都是将compare_exchange_weak放在一个while循环里面循环调用的,如下所示:
while (!atomic_var.compare_exchange_weak(old_value, new_value))
{
// ...
}
当然,你用compare_exchange_strong也可以,但是,很显然 compare_exchange_weak 已经够用了,反正都有循环检测,理论上性能还会更好(为何说理论上,后面看看各系统的实现就能知道)。
备注:
1. 例如ARM 、 Itanium 、 Power PC这些CPU架构就是弱内存顺序的而Inter x86就是强内存顺序架构。它们的内存操作顺序需要借助内存栅栏来控制。细节请见:std::memory_order - cppreference.com
2. 基于上面的代码说明, 即便atomic_0 和 tst_val两个变量实际内存中的值相等,函数的返回值也是false。这就是伪失败,这时候一句代码搞不定,就需要循环调用才能拿到正确的结果。
看看源码,更清晰。
先看LInux GCC的代码实现。
_GLIBCXX_ALWAYS_INLINE bool
compare_exchange_weak(__int_type& __i1, __int_type __i2,
memory_order __m1,
memory_order __m2) volatile noexcept
{
__glibcxx_assert(__is_valid_cmpexch_failure_order(__m2));
return __atomic_compare_exchange_n(&_M_i, &__i1, __i2, 1, int(__m1), int(__m2));
}
_GLIBCXX_ALWAYS_INLINE bool
compare_exchange_strong(__int_type& __i1, __int_type __i2,
memory_order __m1, memory_order __m2) noexcept
{
__glibcxx_assert(__is_valid_cmpexch_failure_order(__m2));
return __atomic_compare_exchange_n(&_M_i, &__i1, __i2, 0, int(__m1), int(__m2));
}
gcc是通过系统内置的 __atomic_compare_exchange_n 函数来实现compare_exchange_weak和compare_exchange_strong功能。__atomic_compare_exchange_n 函数定义如下:
bool __atomic_compare_exchange_n (type *ptr, type *expected, type desired, bool weak, int success_memorder, int failure_memorder)
由第四个bool类型的参数 weak,来确定是实际执行strong还是weak操作。细节请见:__atomic Builtins (Using the GNU Compiler Collection (GCC))
再来看看Windows MSVC 的代码实现
bool compare_exchange_strong(_TVal& _Expected, const _TVal _Desired,
const memory_order _Order = memory_order_seq_cst) noexcept { // CAS with given memory order
long _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation
long _Prev_bytes;
_ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange, _Atomic_address_as(_Storage),
_Atomic_reinterpret_as(_Desired), _Expected_bytes);
if (_Prev_bytes == _Expected_bytes) {
return true;
}
_CSTD memcpy(_STD addressof(_Expected), &_Prev_bytes, sizeof(_TVal));
return false;
}
bool compare_exchange_weak(_Ty& _Expected, const _Ty _Desired, const memory_order _Success,
const memory_order _Failure) volatile noexcept {
static_assert(_Deprecate_non_lock_free_volatile<_Ty>, "Never fails");
return this->compare_exchange_strong(_Expected, _Desired, _Combine_cas_memory_orders(_Success, _Failure));
}
bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Success,
const memory_order _Failure) volatile noexcept {
static_assert(_Deprecate_non_lock_free_volatile<_Ty>, "Never fails");
return this->compare_exchange_strong(_Expected, _Desired, _Combine_cas_memory_orders(_Success, _Failure));
}
MSVC 中compare_exchange_weak和compare_exchange_strong的实现,更狠。直接就compare_exchange_weak调用了ompare_exchange_strong,而ompare_exchange_strong调用_InterlockedCompareExchange这个系统内置API,这个api实现如下:
__MACHINE(long __MACHINECALL_CDECL_OR_DEFAULT _InterlockedCompareExchange(long volatile * _Destination, long _Exchange, long _Comparand))
什么weak、strong,不重要了。可能是因为Windows系统程序就是Intel架构系的吧。这也是上面有"理论上"三个字。
还有些细节,可以这位大神的文章:c++并发编程3. CAS原语 - 知乎 这里就不赘述了。