不太明白 std::atomic::compare_exchange_weak 和 compare_exchange_strong 的逻辑
Don't really get the logic of std::atomic::compare_exchange_weak and compare_exchange_strong
我读过https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
Atomically compares the object representation (until C++20)value
representation (since C++20) of *this with that of expected, and if
those are bitwise-equal, replaces the former with desired (performs
read-modify-write operation). Otherwise, loads the actual value stored
in *this into expected (performs load operation).
据我了解,代码如
bool expected=true;
extern atomic<bool> b = false;
void foo ()
{
//
while(!b.compare_exchange_weak(expected, false));
//
}
循环后将 运行 一次(忽略虚假故障)它将失败,并将写入预期的 false
,因此在第二次迭代中 compare_exchange_weak 将 return 成功,但 b
尚未更改为 true。但这一切有什么意义呢?我虽然可以将它用作同步锁,等待其他线程更改 b
,但现在我想不出它的用法。
cppreference 中的示例还表明,两次调用后 compare_exchange_strong 会成功。
#include <atomic>
#include <iostream>
std::atomic<int> ai;
int tst_val= 4;
int new_val= 5;
bool exchanged= false;
void valsout()
{
std::cout << "ai= " << ai
<< " tst_val= " << tst_val
<< " new_val= " << new_val
<< " exchanged= " << std::boolalpha << exchanged
<< "\n";
}
int main()
{
ai= 3;
valsout();
// tst_val != ai ==> tst_val is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
// tst_val == ai ==> ai is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
}
结果:
ai= 3 tst_val= 4 new_val= 5 exchanged= false
ai= 3 tst_val= 3 new_val= 5 exchanged= false
ai= 5 tst_val= 3 new_val= 5 exchanged= true
我将给出一个我确实使用过的例子,因为它非常简单。
我有一个原子,它描述了某物的可用大小。
有整数溢出的危险,所以我在减去一个值之前先检查。
我的生产代码不完全复制粘贴:
class LockFreeCuncuretSizeLimit {
public:
explicit LockFreeCuncuretSizeLimit(size_t available) : mSize{available}
{}
bool tryAcuqire(size_t s) {
size_t lastSize = mSize;
size_t newSize;
do
{
if (lastSize >= s)
{
newSize = lastSize - s;
}
else
{
return false;
}
}
while (!mSize.compare_exchange_weak(lastSize, newSize));
return true;
}
void release(size_t s) {
mSize += s; // here I do not have worry about integer overflow
}
private:
std::atomic<size_t> mSize;
};
现在尝试图像在没有 compare_exchange_strong
且没有竞争条件的情况下执行此操作。
有一个条件满足的变化,但是当对原子进行减法时,其他线程已经减去一个值,所以当我进行实际减法时,可能会溢出整数。所以如果没有 compare_exchange_strong
.
是做不到的
现在 compare_exchange_strong
和 compare_exchange_weak
之间的区别很难解释。甚至 Herb Sutter 在一些 cppcon 谈话中也放弃了解释并提供了简单的规则:“如果你需要循环使用 compare_exchange_weak
否则使用 compare_exchange_strong
”。
std::atomic::compare_exchange_weak
以线程感知方式执行此英语语言任务:
Because the variable holds expected
, it should now hold desired
.
作为一项微不足道的任务,假设您的 std::atomic<int> x
的值应该是 的平方。但是其他线程可能正在修改它,因此您不能简单地读取该值,将其平方并写回新值。它可能已经改变了!
这是执行此任务的线程安全方法。您保证存储的值将被替换为它的平方。
int expected = x;
while( !x.compare_exchange_weak(expected, expected*expected) );
除非值已更改,否则此代码将自动将 expected
替换为其正方形 。
如果值已更改,expected
现在会更新为新值并且代码会再次尝试。
我读过https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
Atomically compares the object representation (until C++20)value representation (since C++20) of *this with that of expected, and if those are bitwise-equal, replaces the former with desired (performs read-modify-write operation). Otherwise, loads the actual value stored in *this into expected (performs load operation).
据我了解,代码如
bool expected=true;
extern atomic<bool> b = false;
void foo ()
{
//
while(!b.compare_exchange_weak(expected, false));
//
}
循环后将 运行 一次(忽略虚假故障)它将失败,并将写入预期的 false
,因此在第二次迭代中 compare_exchange_weak 将 return 成功,但 b
尚未更改为 true。但这一切有什么意义呢?我虽然可以将它用作同步锁,等待其他线程更改 b
,但现在我想不出它的用法。
cppreference 中的示例还表明,两次调用后 compare_exchange_strong 会成功。
#include <atomic>
#include <iostream>
std::atomic<int> ai;
int tst_val= 4;
int new_val= 5;
bool exchanged= false;
void valsout()
{
std::cout << "ai= " << ai
<< " tst_val= " << tst_val
<< " new_val= " << new_val
<< " exchanged= " << std::boolalpha << exchanged
<< "\n";
}
int main()
{
ai= 3;
valsout();
// tst_val != ai ==> tst_val is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
// tst_val == ai ==> ai is modified
exchanged= ai.compare_exchange_strong( tst_val, new_val );
valsout();
}
结果:
ai= 3 tst_val= 4 new_val= 5 exchanged= false
ai= 3 tst_val= 3 new_val= 5 exchanged= false
ai= 5 tst_val= 3 new_val= 5 exchanged= true
我将给出一个我确实使用过的例子,因为它非常简单。
我有一个原子,它描述了某物的可用大小。 有整数溢出的危险,所以我在减去一个值之前先检查。
我的生产代码不完全复制粘贴:
class LockFreeCuncuretSizeLimit {
public:
explicit LockFreeCuncuretSizeLimit(size_t available) : mSize{available}
{}
bool tryAcuqire(size_t s) {
size_t lastSize = mSize;
size_t newSize;
do
{
if (lastSize >= s)
{
newSize = lastSize - s;
}
else
{
return false;
}
}
while (!mSize.compare_exchange_weak(lastSize, newSize));
return true;
}
void release(size_t s) {
mSize += s; // here I do not have worry about integer overflow
}
private:
std::atomic<size_t> mSize;
};
现在尝试图像在没有 compare_exchange_strong
且没有竞争条件的情况下执行此操作。
有一个条件满足的变化,但是当对原子进行减法时,其他线程已经减去一个值,所以当我进行实际减法时,可能会溢出整数。所以如果没有 compare_exchange_strong
.
现在 compare_exchange_strong
和 compare_exchange_weak
之间的区别很难解释。甚至 Herb Sutter 在一些 cppcon 谈话中也放弃了解释并提供了简单的规则:“如果你需要循环使用 compare_exchange_weak
否则使用 compare_exchange_strong
”。
std::atomic::compare_exchange_weak
以线程感知方式执行此英语语言任务:
Because the variable holds
expected
, it should now holddesired
.
作为一项微不足道的任务,假设您的 std::atomic<int> x
的值应该是 的平方。但是其他线程可能正在修改它,因此您不能简单地读取该值,将其平方并写回新值。它可能已经改变了!
这是执行此任务的线程安全方法。您保证存储的值将被替换为它的平方。
int expected = x;
while( !x.compare_exchange_weak(expected, expected*expected) );
除非值已更改,否则此代码将自动将 expected
替换为其正方形 。
如果值已更改,expected
现在会更新为新值并且代码会再次尝试。