C++ 仅编译器的内存屏障(如std::atomic\u signal\u fence)何时有用?
当我阅读有关内存模型、障碍、排序、原子等的文章时,经常会想到编译器围栏的概念,但通常情况下,它也与CPU围栏配对,正如人们所期望的那样 然而,我偶尔会读到一些关于围栏结构的文章,这些结构只适用于编译器。这方面的一个例子是C++11C++ 仅编译器的内存屏障(如std::atomic\u signal\u fence)何时有用?,c++,c++11,atomic,memory-barriers,memory-fences,C++,C++11,Atomic,Memory Barriers,Memory Fences,当我阅读有关内存模型、障碍、排序、原子等的文章时,经常会想到编译器围栏的概念,但通常情况下,它也与CPU围栏配对,正如人们所期望的那样 然而,我偶尔会读到一些关于围栏结构的文章,这些结构只适用于编译器。这方面的一个例子是C++11std::atomic_signal_fence函数,其状态为: std::atomic_signal_fence与std::atomic_thread_fence等效,只是没有CPU 发布了内存排序说明。只有重新排序 编译器的指令将按顺序指示被抑制 我有五个与此主题相
std::atomic_signal_fence
函数,其状态为:
std::atomic_signal_fence与std::atomic_thread_fence等效,只是没有CPU
发布了内存排序说明。只有重新排序
编译器的指令将按顺序指示被抑制
我有五个与此主题相关的问题:
std::atomic_signal_fence
所暗示的那样,异步中断(例如内核抢占线程以执行信号处理程序)是否仅是编译器隔离有用的情况x86
std::atomic_signal_fence
时,使用acq_rel
和seq_cst
排序之间有什么区别吗?(我希望这不会有什么不同。)thread\u local
访问的围栏?(如果可能的话,我希望编译器专用的围栏,比如atomic\u signal\u fence
是首选的工具。)谢谢。回答所有5个问题:
1) 编译器围栏(本身,没有CPU围栏)仅在两种情况下有用:
- 在单个线程和绑定到同一线程(如信号处理程序)的异步中断处理程序之间执行内存顺序约束
- 当保证每个线程将在同一CPU内核上执行时,在多个线程之间强制执行内存顺序约束。换句话说,应用程序将只在系统上运行,或者应用程序采取特殊措施(通过)以确保共享数据的每个线程都绑定到同一个核心
2) 底层体系结构的内存模型,无论是强序还是弱序,都与某种情况下是否需要编译器隔离无关
3) 下面是伪代码,它演示了如何使用编译器围栏来充分同步线程与绑定到同一线程的异步信号处理程序之间的内存访问:
void async_signal_handler()
{
if ( is_shared_data_initialized )
{
compiler_only_memory_barrier(memory_order::acquire);
... use shared_data ...
}
}
void main()
{
// initialize shared_data ...
shared_data->foo = ...
shared_data->bar = ...
shared_data->baz = ...
// shared_data is now fully initialized and ready to use
compiler_only_memory_barrier(memory_order::release);
is_shared_data_initialized = true;
}
重要提示:此示例假定异步信号处理程序
绑定到初始化共享数据
并设置is\u initialized
标志的同一线程,这意味着应用程序是单线程的,或者它相应地设置线程信号掩码。否则,编译器围栏将不足,还需要CPU围栏
4) 它们应该是相同的。
acq_rel
和seq_cst
都应该产生一个完整的(双向)编译器围栏,没有发出围栏相关的CPU指令。“顺序一致性”的概念只有在涉及多个内核和线程时才起作用,而原子信号围栏只涉及一个执行线程
5) 否。(当然,除非线程本地数据是从异步信号处理程序访问的,在这种情况下可能需要编译器围栏)。否则,由于编译器(和CPU)的原因,线程本地数据永远不需要围栏从单线程的角度来看,只允许以不改变程序相对于其可观察行为的方式对内存访问进行重新排序。在逻辑上,我们可以认为多线程程序中的线程局部静态与单线程程序中的全局静态相同。在这两种情况下,数据只能从单个线程访问,这就防止了数据竞争的发生。实际上有一些不可移植但很有用的C编程习惯用法,其中编译器围栏很有用,即使在多核代码中(尤其是在C11之前的代码中)。典型的情况是,程序正在执行一些通常会变得不稳定的访问(因为它们是对共享变量的访问),但您希望编译器能够移动这些访问。如果您知道访问在目标平台上是原子的(并且您采取了一些其他预防措施),则可以使访问保持非易失性,但使用编译器屏障包含代码移动
谢天谢地,C11/C++11轻松原子使大多数类似的编程变得过时。您检查过了吗。引用preshing.com:“正如我提到的,编译器屏障足以防止单处理器系统上的内存重新排序。但现在是2012年,而如今,多核计算已成为常态。如果我们想确保在多处理器环境中以及在任何CPU体系结构上,我们的交互都以所需的顺序进行,那么编译器屏障是不够的。[…]”“@chico:很好的一点-如果程序员知道应用程序将只在非SMP系统上运行(即,单核单CPU或内核中由于某种原因禁用SMP),这是编译器不可能知道或假设的,那么
原子信号围栏
(或某些其他编译器专用围栏构造)可以用作潜在的优化。正如本文所述,Linux内核具有