繁体   English   中英

我是否需要使用内存屏障来保护共享资源?

[英]Do I need to use memory barriers to protect a shared resource?

在多生产者,多消费者的情况下。 如果生产者写入int a ,并且消费者正在读取int a ,我是否需要围绕int a内存屏障?

我们都了解到:共享资源应该始终受到保护,标准不能保证正确的行为。

然而,在高速缓存一致的体系结构上,可以自动确保可见性,并且保证8,16,32和64位变量的原子性MOV操作。

因此,为什么要保护int a

至少在C ++ 11(或更高版本)中,您不需要(明确地)使用互斥锁或内存屏障保护您的变量。

您可以使用std::atomic来创建原子变量。 对该变量的更改保证跨线程传播。

std::atomic<int> a;

// thread 1:
a = 1;

// thread 2 (later):
std::cout << a;    // shows `a` has the value 1.

当然,还有更多的东西 - 例如,不能保证std::cout原子地工作,所以你可能必须保护它(如果你试图从多个线程写入,无论如何)。

然后由编译器/标准库来确定处理原子性要求的最佳方法。 在确保高速缓存一致性的典型体系结构中,它可能仅仅意味着“不要在寄存器中分配此变量”。 它可能会造成内存障碍,但只能在真正需要它们的系统上实现。

然而,在高速缓存一致的体系结构上,可以自动确保可见性,并且保证8,16,32和64位变量的原子性MOV操作。

除非您严格遵守C ++规范的要求以避免数据争用,否则编译器没有义务按照它的方式使您的代码功能化。 例如:

int a = 0, b = 0; // shared variables, initialized to zero

a = 1;
b = 1;

假设您在完全缓存一致的架构上执行此操作。 在这样的硬件上,似乎因为a是在b之前编写的,所以没有线程将能够看到值为1的b而没有具有该值。

但这种情况并非如此。 如果您未能严格遵守C ++内存模型的要求以避免数据争用,例如,如果没有在任何地方插入正确的同步原语而读取这些变量,那么您的程序实际上可能会在a之前观察到b被写入。 原因是您已经引入了“未定义的行为”,并且C ++实现没有义务对您做任何有意义的事情。

在实践中可能发生的是, 编译器可能会重新排序写入,即使硬件非常难以使其看起来好像所有写入都按照执行写入的机器指令的顺序发生。 您需要整个工具链才能进行合作,而仅仅通过硬件进行协作(例如强大的缓存一致性)是不够的。


如果您想了解C ++内存模型的细节并在C ++中编写可移植的并发代码,那么C ++ Concurrency in Action是一个很好的资源。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM