不,您仍然可以有条件地使用 std::atomic 。
首先, for 的溢出行为.fetch_add被明确定义为原子类型的无符号或2 的补码环绕(尽管可能不是您想要的)。与非原子类型不同,整数溢出仍然是 UB。
你是对的foo++,然后单独读取foo将是一个错误,因此请int tmp = foo++;使用原子 RMW 操作的返回值。
如果您绝对必须检查溢出,或以其他方式有条件地执行操作,则可以使用比较交换。这使您可以读取该值,决定是否要对其进行处理,然后自动更新该值(如果它没有更改)。这里的关键部分是系统会告诉你原子更新是否失败,在这种情况下你可以返回到开始并读取新值并再次做出决定。
举个例子,如果我们只想将原子整数的最大值设置为 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_weak(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
}
Run Code Online (Sandbox Code Playgroud)
像这样的循环通常写成do{...}while(!refcount.compare_exchange_weak(...));
我们使用compare_exchange_weak而不是compare_exchange_strong. 有时这可能会错误地失败,因此您需要循环执行此操作。然而,无论如何我们都有一个循环(一般来说,当您需要处理真正的故障时,您总是会这样做),因此compare_exchange_weak这里很有意义。
在某些 ISA 上,需要使用 asm 循环来实现,compare_exchange_strong但不需要weak;在 x86 等其他 ISA 上,两者都编译为相同的 asm。weak只要为下一次尝试做准备的代码很便宜,就像在本例中一样,使用它不会让任何事情变得更糟compare_exchange_weak。
| 归档时间: |
|
| 查看次数: |
876 次 |
| 最近记录: |