C++ std::原子加载和存储都是必需的吗?

C++ std::原子加载和存储都是必需的吗?,c++,multithreading,c++11,interrupt,atomic,C++,Multithreading,C++11,Interrupt,Atomic,根据: 每当两个线程同时对共享变量进行操作,并且其中一个操作执行写入操作时,两个线程都必须使用原子操作 但是,如果低优先级线程是写入线程,而高优先级线程是读线程,那么低优先级线程是否需要强制执行原子存储?在我看来,只有更高优先级的线程才需要强制原子负载: #include <atomic> std::atomic<T*> ptr; // assume initialized to some non-null value void highPriThreadFcn(vo

根据:

每当两个线程同时对共享变量进行操作,并且其中一个操作执行写入操作时,两个线程都必须使用原子操作

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

#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
}

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

实际上是一个顺序一致的原子存储。看


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

该标准设计为在具有怪异内存语义的平台上受支持。它也适应了这一点,避免了对实现如何处理某些平台难以预料的任何构造强加任何要求。一些实现将此理解为在这种情况下以任意和不可预测的方式进行操作的许可,即使是针对自然支持更强内存语义的平台。这有助于进行某些类型的优化,但可能使程序员有必要采取明确的行动来束缚优化器的手脚,在某些情况下,如果编译器提供(和程序员利用)比标准要求更强大的语义,就会降低效率

作为一个事例可能出错的例子,考虑一下:

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)
替换最后一个函数调用,这是完全合理的。该标准的作者没有使用允许有界非确定性的抽象模型,而是拒绝强制要求对对象值意外更改的影响提供任何行为保证。

当您谈到优先级时,您是指操作系统调度程序给不同线程的时间段吗?如果是这样,那又有什么关系呢?该优先级可以在程序运行时更改,以使低优先级线程变为高优先级线程。此外,您似乎以原子方式加载/存储指针,但如何访问指针指向的数据?@TEDLYNGOM关于您的前两个问题,我想我说的是“固定”优先级(例如RTOS配置中分配的静态优先级,或CPU的中断优先级)。关于最后一个问题,除非它影响到问题,否则我不认为有必要指定,但假设在加载后指针将被取消引用(这可能需要内存限制)。@harold Yes,我指的是单核系统,主程序作为“低优先级线程”,异步硬件中断作为“更高优先级线程”+1,对于加载对应线程。谢谢。我发现了一些信息,表明了相同的情况(即,无论是否显式使用.load()/.store(),原子的读/写仍然被视为这样),但我没有建立它在本例中应用的联系。我不明白你的例子的问题是什么,假设y不是易变的。想详细说明一下吗?它与“有界非确定性”有关吗(我不知道那是什么)?@abc:supercat建议编译器将
foo
的读取时间推迟很久。这很重要,因为
foo
对其他代码(可能包括ISR)是可见的。如果存在竞争,并且
foo
是原子的,那么结果很难预测,但与可能的顺序一致。在这段代码中
foo
不是原子的,所以任何数据竞争都会导致UB。@BenVoigt啊,我明白了。因此,原子的所有读/写都是隐式原子加载/存储,以防编译器进行意外的重新排序(或优化出)基于预期的原子行为可能“破坏”事物的事物(例如,如果一个人显式调用原子加载/存储,但结果并不重要,但由于编译器重新安排而无法进行显式加载/存储调用,则最终必须如此)@abc:在一个允许有界非确定性的抽象模型中,读取一个受竞争条件约束的对象将产生一个非确定性值,该值表现为该对象可能持有的所有值的叠加,但对于没有陷阱表示的类型,将不会有其他副作用。如果这样一个抽象模型提供了一个intrinsic给定一个可能不确定的值,将以未指定的方式从它可能持有的值中选择一个值,程序员可以允许编译器比今天的标准更灵活。
ptr = some_ptr;
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);