C++ 如何在一个线程中存储内存;迅速地;在其他线程中可见?
假设我想将设备寄存器的内容复制到一个变量中,该变量将由多个线程读取。有没有一个好的通用方法?以下是两种可能的方法的示例:C++ 如何在一个线程中存储内存;迅速地;在其他线程中可见?,c++,multithreading,c++11,atomic,stdatomic,C++,Multithreading,C++11,Atomic,Stdatomic,假设我想将设备寄存器的内容复制到一个变量中,该变量将由多个线程读取。有没有一个好的通用方法?以下是两种可能的方法的示例: #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;
#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);
#包括
volatile int*const Device_reg_ptr=reinterpret_cast(0x666);
//此变量由多个线程读取。
std::原子设备\u注册表\u副本;
// ...
//方法1
const_cast(设备注册副本)
.store(*设备注册ptr,标准::内存顺序\u松弛);
//方法2
device_reg_copy.store(*device_reg_ptr,std::memory_order_released);
std::原子线程围栏(std::内存命令释放);
更一般地说,在面对可能的整个程序优化时,如何正确地控制一个线程中的内存写入在其他线程中可见的延迟
编辑:在您的回答中,请考虑以下情况:
- 代码在嵌入式系统的CPU上运行
- 单个应用程序在CPU上运行
- 应用程序的线程数远远少于CPU的处理器核数
- 每个核心都有大量的寄存器
- 该应用程序足够小,在构建其可执行文件时可以成功地使用整个程序优化
如何确保一个线程中的存储不会无限期地对其他线程不可见?如果要以原子方式更新
device\u reg\u copy
的值,则device\u reg\u copy.store(*device\u reg\u ptr,std::memory\u order\u released)代码>足够了
不需要将volatile
应用于原子变量,这是不必要的
std::memory\u order\u released
存储应该产生最少的同步开销。在x86上,它只是一条普通的mov
指令
但是,如果您希望以这样的方式更新它,使任何先前存储的效果与新值device\u reg\u copy
一起对其他线程可见,则使用std::memory\u order\u release
store,即device\u reg\u copy.store(*device\u reg\u ptr,std::memory\u order\u release)代码>。在这种情况下,读卡器需要按照std::memory\u order\u acquire
加载device\u reg\u copy
。同样,在x86std::memory\u order\u release
上,存储是一个普通的mov
然而,如果您使用最昂贵的std::memory_order_seq_cst
存储,它会为您在x86上插入内存屏障
这就是为什么他们说x86内存模型对于C++11来说有点太强了:普通的mov
指令是std::memory\u order\u release
存储和std::memory\u order\u acquire
。x86上没有宽松的存储或加载
我无法推荐足够的文章。如果您想以原子方式更新设备注册副本
的值,那么设备注册副本.store(*device注册副本,std::memory\u order\u released)代码>足够了
不需要将volatile
应用于原子变量,这是不必要的
std::memory\u order\u released
存储应该产生最少的同步开销。在x86上,它只是一条普通的mov
指令
但是,如果您希望以这样的方式更新它,使任何先前存储的效果与新值device\u reg\u copy
一起对其他线程可见,则使用std::memory\u order\u release
store,即device\u reg\u copy.store(*device\u reg\u ptr,std::memory\u order\u release)代码>。在这种情况下,读卡器需要按照std::memory\u order\u acquire
加载device\u reg\u copy
。同样,在x86std::memory\u order\u release
上,存储是一个普通的mov
然而,如果您使用最昂贵的std::memory_order_seq_cst
存储,它会为您在x86上插入内存屏障
这就是为什么他们说x86内存模型对于C++11来说有点太强了:普通的mov
指令是std::memory\u order\u release
存储和std::memory\u order\u acquire
。x86上没有宽松的存储或加载
<>我不能推荐足够的文章。 < P>对其他线程可见原子存储库的C++标准很模糊。
29.3.12
实现应该使原子存储在合理的时间内对原子负载可见
这是非常详细的,没有“合理”的定义,也不需要立即定义
没有必要使用独立的围栏强制执行特定的内存排序,因为您可以在原子操作上指定这些顺序,但问题是,
关于使用内存围栏,您的期望是什么?
Fences设计用于强制内存操作(线程之间)的排序,但它们不能保证及时的可见性。
您可以使用最强的内存顺序(即seq_cst
)将值存储到原子变量中,但即使另一个线程在比store()
更晚的时间执行load()
,
您可能仍然从缓存中获得一个旧值,但(令人惊讶的是)它并没有违反“发生在之前”关系。
使用更坚固的栅栏可能会有所不同。时间和可视性,但没有保证
<>如果即时可见性很重要,我会考虑使用读修改写(RMW)操作来加载该值。
这些是以原子方式读取和修改(即在单个调用中)的原子操作,并具有保证在最新值上操作的附加属性。
但由于它们必须到达比本地缓存更远的地方,因此这些调用的执行成本也往往更高
正如Maxim Egorushkin所指出的,您是否可以使用比默认值更弱的内存顺序(// 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);