编译器内存屏障和互斥锁

use*_*878 11 c++ compiler-construction multithreading

posix标准说像mutex这样的东西会强制执行内存同步.但是,编译器可能会重新排序内存访问.说我们有

lock(mutex);
setdata(0);
ready = 1;
unlock(mutex);
Run Code Online (Sandbox Code Playgroud)

可能会通过编译器重新排序将其更改为下面的代码,对吗?

ready = 1;
lock(mutex);
setdata(0);
unlock(mutex);
Run Code Online (Sandbox Code Playgroud)

那么互斥量如何同步内存访问?更确切地说,编译器如何知道重锁不应该在锁定/解锁时发生?

实际上这里对于单线程方面,就绪分配重新排序是完全安全的,因为就绪不用于函数调用锁(mutex).

编辑:因此,如果函数调用是编译器无法解决的问题,我们可以将其视为编译器内存屏障

asm volatile("" ::: "memory")
Run Code Online (Sandbox Code Playgroud)

Ant*_*nko 8

一般的答案是,如果你想将它用于POSIX目标,你的编译器应该支持POSIX,而这种支持意味着它应该知道避免重新排序锁定和解锁.

也就是说,这种知识通常是以一种微不足道的方式实现的:编译器不会通过调用可能使用或修改它们的外部函数来重新排序对(不可证实 - 本地)数据的访问.它应该知道一些特别的东西,lockunlock能够重新排序.

不,它并不是那么简单,因为"对全局函数的调用始终是编译器障碍" - 我们应该添加"除非编译器知道关于该函数的具体内容".它确实发生了:例如pthread_self在Linux(NPTL)上声明了__const__属性,允许gcc重新排序pthread_self()调用,甚至完全消除不必要的调用.

我们可以很容易地想象一个编译器支持获取/释放语义的函数属性,lockunlock不是完整的编译器障碍.

  • @mah参见[这篇文章](http://newsgroups.derkeiler.com/Archive/Comp/comp.programming.threads/2007-11/msg00006.html),了解Dave Butenhof对POSIX与编译器关系的看法. (4认同)