[英]Do I need to use memory barriers to protect a shared resource?
In a multi-producer, multi-consumer situation. 在多生产者,多消费者的情况下。 If producers are writing into
int a
, and consumers are reading from int a
, do I need memory barriers around int a
? 如果生产者写入
int a
,并且消费者正在读取int a
,我是否需要围绕int a
内存屏障?
We all learned that: Shared resources should always be protected and the standard does not guarantee a proper behavior otherwise. 我们都了解到:共享资源应该始终受到保护,标准不能保证正确的行为。
However on cache-coherent architectures visibility is ensured automatically and atomicity of 8, 16, 32 and 64 bit variables MOV
operation is guaranteed. 然而,在高速缓存一致的体系结构上,可以自动确保可见性,并且保证8,16,32和64位变量的原子性
MOV
操作。
Therefore, why protect int a
at all? 因此,为什么要保护
int a
?
At least in C++11 (or later), you don't need to (explicitly) protect your variable with a mutex or memory barriers. 至少在C ++ 11(或更高版本)中,您不需要(明确地)使用互斥锁或内存屏障保护您的变量。
You can use std::atomic
to create an atomic variable. 您可以使用
std::atomic
来创建原子变量。 Changes to that variable are guaranteed to propagate across threads. 对该变量的更改保证跨线程传播。
std::atomic<int> a;
// thread 1:
a = 1;
// thread 2 (later):
std::cout << a; // shows `a` has the value 1.
Of course, there's a little more to it than that--for example, there's no guarantee that std::cout
works atomically, so you probably will have to protect that (if you try to write from more than one thread, anyway). 当然,还有更多的东西 - 例如,不能保证
std::cout
原子地工作,所以你可能必须保护它(如果你试图从多个线程写入,无论如何)。
It's then up to the compiler/standard library to figure out the best way to handle the atomicity requirements. 然后由编译器/标准库来确定处理原子性要求的最佳方法。 On a typical architecture that ensures cache coherence, it may mean nothing more than "don't allocate this variable in a register".
在确保高速缓存一致性的典型体系结构中,它可能仅仅意味着“不要在寄存器中分配此变量”。 It could impose memory barriers, but is only likely to do so on a system that really requires them.
它可能会造成内存障碍,但只能在真正需要它们的系统上实现。
However on cache-coherent architectures visibility is ensured automatically and atomicity of 8, 16, 32 and 64 bit variables MOV operation is guaranteed.
然而,在高速缓存一致的体系结构上,可以自动确保可见性,并且保证8,16,32和64位变量的原子性MOV操作。
Unless you strictly adhere to the requirements of the C++ spec to avoid data races, the compiler is not obligated to make your code function the way it appears to. 除非您严格遵守C ++规范的要求以避免数据争用,否则编译器没有义务按照它的方式使您的代码功能化。 For example:
例如:
int a = 0, b = 0; // shared variables, initialized to zero
a = 1;
b = 1;
Say you do this on your fully cache-coherent architecture. 假设您在完全缓存一致的架构上执行此操作。 On such hardware it would seem that since a is written before b no thread will ever be able to see b with a value of 1 without a also having that value.
在这样的硬件上,似乎因为a是在b之前编写的,所以没有线程将能够看到值为1的b而没有具有该值。
But this is not the case. 但这种情况并非如此。 If you have failed to strictly adhere to the requirements of the C++ memory model for avoiding data races, eg you read these variables without the correct synchronization primitives being inserted anywhere, then your program may in fact observe b being written before a.
如果您未能严格遵守C ++内存模型的要求以避免数据争用,例如,如果没有在任何地方插入正确的同步原语而读取这些变量,那么您的程序实际上可能会在a之前观察到b被写入。 The reason is that you have introduce "undefined behavior" and the C++ implementation has no obligation to do anything that makes sense to you.
原因是您已经引入了“未定义的行为”,并且C ++实现没有义务对您做任何有意义的事情。
What may be going on in practice, is that the compiler may reorder writes even if the hardware works very hard to make it seem as if all writes occur in the order of the machine instructions performing the writes. 在实践中可能发生的是, 编译器可能会重新排序写入,即使硬件非常难以使其看起来好像所有写入都按照执行写入的机器指令的顺序发生。 You need the entire toolchain to cooperate, and cooperation from just the hardware, such as strong cache coherency, is not sufficient.
您需要整个工具链才能进行合作,而仅仅通过硬件进行协作(例如强大的缓存一致性)是不够的。
The book C++ Concurrency in Action is a good source if you care to learn about the details of the C++ memory model and writing portable, concurrent code in C++. 如果您想了解C ++内存模型的细节并在C ++中编写可移植的并发代码,那么C ++ Concurrency in Action是一个很好的资源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.