例如,您可以安全地递增和递减 std::atomic_int。但是,如果您需要检查溢出或根据值有条件地执行一些例程,那么无论如何都需要锁定。由于您必须比较该值,并且线程可能在比较成功后立即被交换,另一个线程修改,......错误。
但是如果你需要一个锁,那么你可以只使用一个普通的整数而不是原子的。我对吗?
不,您仍然可以有条件地使用 std::atomic 。
首先,如果您使用,std::atomic<unsigned int>
则溢出行为是明确定义的(尽管可能不是您想要的)。如果您使用带符号整数溢出的定义不明确,但只要您不点击它,那么这无关紧要。
如果你绝对必须检查溢出,或者有条件地采取行动,你可以使用 compare-exchange。这使您可以读取该值,决定是否要对其进行处理,然后如果该值未更改,则以原子方式更新该值。而这里的关键部分是系统会告诉你原子更新是否失败,在这种情况下,你可以回到开始并读取新值并再次做出决定。
例如,如果我们只想将原子整数的最大值设置为 4(例如,在某种引用计数中),我们可以这样做:
#include <atomic>
static std::atomic<int> refcount = 0;
int val = refcount; // this doesn't need to be in the loop as on failure compare-exchange-strong updates it
while(true)
{
if(val == 4)
{
// there's already 4 refs here, maybe come back later?
break;
}
int toChangeTo = val + 1;
if(refcount.compare_exchange_strong(val, toChangeTo))
{
// we successfully took a ref!
break;
}
// if we fail here, another thread updated the value whilst we were running, just loop back and try again
}
在上面的代码中,您可以compare_exchange_weak
改用。这有时会虚假地失败,因此您需要循环执行。但是,无论如何,我们都有一个循环(通常你总是会因为你需要处理真正的故障),所以compare_exchange_weak
在这里很有意义。