std::原子加载和存储都需要吗

Are std::atomic loads and stores both required?

本文关键字:存储 加载 std      更新时间:2023-10-16

根据本文:

任何时候,两个线程同时对一个共享变量进行操作,并且其中一个操作执行写操作,则两个线程都必须使用原子操作。

但是,如果一个优先级较低的线程是写入程序,而一个优先级较高的线程是读取器,那么低优先级线程是否需要强制执行原子存储?在我看来,只有优先级较高的线程才需要强制执行原子负载:

#include <atomic>
std::atomic<T*> ptr; // assume initialized to some non-null value
void highPriThreadFcn(void)
{
T* local_ptr = ptr.load(); // need atomic load here in case lowPriThread write/store was interrupted
}
void lowPriThreadFcn(T* some_ptr)
{
ptr = some_ptr; // do I need an atomic store here? I'd think not, as ptr is not written to by highPriThread
}

类似的问题也适用于"反向"情况(在高优先级线程中写入,从低优先级线程中读取(:

void highPriThreadFcn(T* some_ptr)
{
ptr = some_ptr; // do I need an atomic store here? I'd think not, as write to ptr cannot be interrupted
}
void lowPriThreadFcn(void)
{
T* local_ptr = ptr.load(); // need atomic load here in case read/load was interrupted
}

不能对原子变量执行非原子存储或加载。API确保没有办法尝试。你所谓的"非原子商店",

ptr = some_ptr;

实际上是一个顺序一致的原子存储。请参阅cppreference上的atomic::operator=。

顺便说一句,如果您正在考虑将原子变量更改为非原子变量,在该变量上只执行一些原子操作:请不要这样做。每当加载和存储到同一内存位置是"潜在并发的"时,标准要求它们是原子的。否则,行为是未定义的。这意味着编译器可以以破坏代码的方式进行"优化"。一个线程的优先级是否比另一个线程"高"并不影响这一点。

标准被设计为可在具有奇怪和奇怪内存语义的平台上得到支持。为了适应这一点,它避免了对实现如何处理任何结构施加任何要求,而这些结构对于一些平台来说可能难以预测地处理。一些实现将其解释为在这种情况下以任意和不可预测的方式行事的许可证,即使是针对自然支持更强内存语义的平台。这有助于某些类型的优化,但可能会使程序员有必要采取明确的行动来束缚优化器的手脚,在某些情况下,如果编译器提供(和程序员利用(比标准要求更强的语义,则会使事情的效率降低。

作为一个可能出现问题的情况的例子,可以考虑以下内容:

extern int foo;
int x=foo;
.... some code that modifies neither x nor foo
int y=x;
.... some code that modifies neither y nor foo (but might modify x)
doSomething(x, y);

在假设foo必须等于y在原始赋值时的值的情况下,优化编译器可以用doSomething(x, foo)替换最后一个函数调用,这是完全合理的。该标准的作者没有使用允许有界非决定论的抽象模型,而是拒绝强制要求对对象值意外变化的影响提供任何形式的行为保证。