如何使一个线程中的内存存储"promptly"在其他线程中可见?
How do I make memory stores in one thread "promptly" visible in other threads?
假设我想将设备寄存器的内容复制到一个将由多个线程读取的变量中。 有没有一个好的通用方法可以做到这一点? 以下是执行此操作的两种可能方法的示例:
#include <atomic>
volatile int * const Device_reg_ptr = reinterpret_cast<int *>(0x666);
// This variable is read by multiple threads.
std::atomic<int> device_reg_copy;
// ...
// Method 1
const_cast<volatile std::atomic<int> &>(device_reg_copy)
.store(*Device_reg_ptr, std::memory_order_relaxed);
// Method 2
device_reg_copy.store(*Device_reg_ptr, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
更一般地说,面对可能的整个程序优化,如何正确控制一个线程中的内存写入延迟在其他线程中可见?
编辑:在您的回答中,请考虑以下情况:
- 代码在嵌入式系统的 CPU 上运行。
- CPU 上运行单个应用程序。
- 应用程序的线程数远远少于 CPU 的处理器内核。
- 每个内核都有大量的寄存器。
- 该应用程序足够小,因此在构建其可执行文件时可以成功使用整个程序优化。
我们如何确保一个线程中的存储不会无限期地对其他线程不可见?
如果您想以原子方式更新device_reg_copy
的值,那么device_reg_copy.store(*Device_reg_ptr, std::memory_order_relaxed);
就足够了。
没有必要将volatile
应用于原子变量,这是不必要的。
std::memory_order_relaxed
存储应该产生最少的同步开销。在x86上,它只是一个普通的mov
指令。
但是,如果您想以这样的方式更新它,以使任何先前存储的效果与新值device_reg_copy
一起对其他线程可见,请使用std::memory_order_release
存储,即device_reg_copy.store(*Device_reg_ptr, std::memory_order_release);
.在这种情况下,读取器需要按std::memory_order_acquire
加载device_reg_copy
。同样,在x86上std::memory_order_release
商店是一个普通的mov
。
而如果您使用最昂贵的std::memory_order_seq_cst
存储,它确实会在x86上为您插入内存屏障。
这就是为什么他们说 x86 内存模型对于 C++11 来说有点太强了:普通mov
指令在存储上std::memory_order_release
,在负载上std::memory_order_acquire
。x86 上没有轻松的存储或负载。
我不能推荐足够的CPU缓存刷新谬误文章。
C++标准在使原子存储对其他线程可见方面相当模糊。
12-3-29 实现应该在合理的时间内使原子存储对原子负载可见。
这是非常详细的,没有"合理"的定义,也不必立即定义。
使用独立围栏来强制特定的内存排序是不必要的,因为您可以在原子操作上指定这些顺序,但问题是, 您对使用内存围栏的期望是什么..
栅栏旨在对内存操作(线程之间)强制排序,但它们不能保证及时的可见性。 您可以将值存储到具有最强内存顺序的原子变量(即。seq_cst
),但即使另一个线程在比store()
更晚的时间执行load()
, 您可能仍然会从缓存中获取旧值,但(令人惊讶的是)它并没有违反发生前关系。 使用更坚固的围栏可能会有所作为。时间和可见性,但无法保证。
如果提示可见性很重要,我会考虑使用读取-修改-写入 (RMW) 操作来加载值。 这些是以原子方式读取和修改的原子操作(即在单个调用中),并具有保证对最新值进行操作的附加属性。 但是,由于它们必须到达比本地缓存更远的地方,因此这些调用的执行成本也往往更高。
正如Maxim Egorushkin所指出的,你是否可以使用比默认值(seq_cst
)弱的内存排序取决于其他内存操作是否需要在线程之间同步(可见)。 从您的问题中不清楚,但通常认为使用默认值(顺序一致性)是安全的。
如果你在一个异常弱的平台上,如果性能有问题,如果你需要在线程之间进行数据同步,你可以考虑使用获取/释放语义:
// thread 1
device_reg_copy.store(*Device_reg_ptr, std::memory_order_release);
// thread 2
device_reg_copy.fetch_add(0, std::memory_order_acquire);
如果线程 2 看到线程 1写入的值,则可以保证线程 1 中存储之前的内存操作在线程 2 中加载后可见。 获取/发布操作形成一对,它们根据存储和负载之间的运行时关系进行同步。换句话说,如果线程 2 看不到线程 1 存储的值, 没有订购保证。
如果原子变量不依赖于任何其他数据,则可以使用std::memory_order_relaxed
;存储顺序始终保证单个原子变量。
正如其他人提到的,在与std::atomic
的线程间通信方面不需要volatile
。
- 类与私有变量的其他类之间的线程安全性
- 当我在其中一个线程执行中(在activemq-cpp中)捕获到特定值时,我如何终止/停止所有其他线程
- MESI协议和std::atomic-它是否确保所有写入立即对其他线程可见?
- 线程消息传递或更好:在"大师班"中访问其他班级的成员
- 锁定来自其他线程的类成员
- 使来自线程的数据流对所有其他线程都可读
- Qt信号和插槽如果从QRunnable或其他线程调用,则不起作用
- 当只有一个线程主要使用该对象而其他线程很少使用它时,如何最小化该对象的互斥锁锁定?
- 视频在唤醒其他线程时输入设备断开连接
- 如何从其他线程 winapi 获取消息
- 将抽象对象从主线程发送到其他线程
- 使用 std::condition_variable 触发其他线程.使用哪些互斥锁?
- 在其他线程中循环访问该concurrent_vector时调用 concurrency::concurrent_vect
- 生成线程并在运行时执行其他操作,只要它处于活动状态
- C++如何判断互斥体在阻塞其他线程时是否被单个线程不成比例地占用
- tbb::enumerable_thread_specific在其他线程库中工作吗
- 当其他线程正在编写线程安全时,我是否必须互斥读操作
- 无法从其他线程播放QMediaPlayer
- 无法修改其他线程中 ref 传递的值
- 其他线程堆栈上的可用内存无效