std::atomic 加载和存储都需要吗?
Are std::atomic loads and stores both required?
根据this article:
Any time two threads operate on a shared variable concurrently, and one of those operations performs a write, both threads must use atomic operations.
但是,如果低优先级线程是写入器,而高优先级线程是 reader,低优先级线程是否需要强制执行原子存储?在我看来,只有更高优先级的线程需要强制执行原子加载:
#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
}
类似的问题适用于 "reverse" 情况(在高优先级线程中写入,从低优先级线程读取):
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 确保没有办法尝试它。据称你 "non-atomic store",
ptr = some_ptr;
实际上是一个顺序一致的原子存储。参见 atomic::operator= on cppreference。
顺便说一下,如果您正在考虑将原子变量更改为仅以原子方式执行某些操作的非原子变量:请不要那样做。每当对同一内存位置的加载和存储是 "potentially concurrent" 时,标准要求它们 都 是原子的。否则,行为未定义。这意味着允许编译器以破坏代码的方式 "optimize" 。一个线程是否比另一个线程 "higher" 优先级不会影响这一点。
该标准旨在支持具有怪异内存语义的平台。为了适应这一点,它避免了对实现如何处理某些平台可能难以预测地处理的任何结构强加任何要求。一些实现将此解释为在这种情况下以任意和不可预测的方式行事的许可即使目标平台自然会支持更强的内存语义。这促进了某些类型的优化,但可能使程序员有必要采取明确的行动来束缚优化器的手,在某些情况下,如果编译器提供(和程序员利用)比标准要求的更强的语义,则效率会降低.
作为可能出错的情况的示例,请考虑以下内容:
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
在原始任务的时间。该标准的作者没有使用允许有界非确定性的抽象模型,而是简单地拒绝强制要求对对象值意外更改的影响进行任何类型的行为保证。
根据this article:
Any time two threads operate on a shared variable concurrently, and one of those operations performs a write, both threads must use atomic operations.
但是,如果低优先级线程是写入器,而高优先级线程是 reader,低优先级线程是否需要强制执行原子存储?在我看来,只有更高优先级的线程需要强制执行原子加载:
#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
}
类似的问题适用于 "reverse" 情况(在高优先级线程中写入,从低优先级线程读取):
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 确保没有办法尝试它。据称你 "non-atomic store",
ptr = some_ptr;
实际上是一个顺序一致的原子存储。参见 atomic::operator= on cppreference。
顺便说一下,如果您正在考虑将原子变量更改为仅以原子方式执行某些操作的非原子变量:请不要那样做。每当对同一内存位置的加载和存储是 "potentially concurrent" 时,标准要求它们 都 是原子的。否则,行为未定义。这意味着允许编译器以破坏代码的方式 "optimize" 。一个线程是否比另一个线程 "higher" 优先级不会影响这一点。
该标准旨在支持具有怪异内存语义的平台。为了适应这一点,它避免了对实现如何处理某些平台可能难以预测地处理的任何结构强加任何要求。一些实现将此解释为在这种情况下以任意和不可预测的方式行事的许可即使目标平台自然会支持更强的内存语义。这促进了某些类型的优化,但可能使程序员有必要采取明确的行动来束缚优化器的手,在某些情况下,如果编译器提供(和程序员利用)比标准要求的更强的语义,则效率会降低.
作为可能出错的情况的示例,请考虑以下内容:
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
在原始任务的时间。该标准的作者没有使用允许有界非确定性的抽象模型,而是简单地拒绝强制要求对对象值意外更改的影响进行任何类型的行为保证。