posix标准说,像互斥锁这样的东西将强制内存同步。但是,编译器可能会对内存访问重新排序。假设我们有
lock(mutex);
setdata(0);
ready = 1;
unlock(mutex);可以通过编译器重新排序将其更改为下面的代码,对吗?
ready = 1;
lock(mutex);
setdata(0);
unlock(mutex);那么互斥锁如何同步内存访问呢?更准确地说,编译器如何知道锁/解锁之间不应该发生重新排序?
实际上,在单线程方面,就绪赋值重新排序是完全安全的,因为在函数调用锁(Mutex)中不使用ready。
编辑:因此,如果函数调用是编译器无法跨越的东西,我们是否可以将其视为编译器内存屏障,如
asm volatile("" ::: "memory")发布于 2013-01-21 19:53:48
一般的答案是,如果您想将POSIX用于POSIX目标,那么编译器应该支持POSIX,而这种支持意味着编译器应该知道如何避免在锁定和解锁之间重新排序。
也就是说,这种知识通常是以一种微不足道的方式实现的:编译器不会在调用可能使用或修改数据的外部函数时重新排序对(非可证明本地的)数据的访问。它应该知道lock和unlock的一些特殊之处,以便能够重新排序。
不,这并不像“调用全局函数总是编译器的障碍”那么简单--我们应该加上“除非编译器知道一些关于该函数的具体信息”。这确实发生了:例如,pthread_self on Linux (NPTL)声明了__const__属性,允许gcc跨pthread_self()调用重新排序,甚至完全消除了不必要的调用。
我们可以很容易地想象一个编译器支持获取/释放语义的函数属性,使得lock和unlock不是一个完整的编译器障碍。
发布于 2013-01-21 19:51:37
编译器不会对不安全的东西进行重新排序。在你的“如果”的例子中,你没有提出重新排序的内存访问,你是在问如果编译器完全改变了代码的顺序--它不会这样做。编译器可能会改变实际内存读/写的顺序,但不会改变函数调用的顺序(不管有没有这些内存访问)。
例如,编译器可能会对内存访问进行重新排序...假设你有这样的代码:
a = *pAddressA;
b = *pAddressB;让我们考虑pAddressB的值在寄存器中,而pAddressA的值不在寄存器中的情况。编译器首先读取地址B,然后将pAddressA的值移动到相同的寄存器中,这样就可以接收新的位置。如果这些访问之间碰巧有函数调用,编译器就不能这样做。
https://stackoverflow.com/questions/14437895
复制相似问题