- 来自Leslie Lamport的经典论文《How to Make a Multiprocessor Computer That Correctly Executes Multiprocess Programs》:
" Consider a computer composed of several such processors accessing a common memory. The customary approach to designing and proving the correctness of multiprocess algorithms for such a computer assumes that the following condition is satisfied : the result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individula processor appear in this sequence in the order specified by its program. A multiprocessor satisfying this condition will be called sequentially consistent." --- LESLIE LAMPORT
【案例1】只有两个进程的互斥协议
process 1 :
a = 1; //1
if (b == 0) {//2
// 进入临界区域
a = 0;
// 退出临界区域
}else {
// 执行其他操作
}
process 2 :
b = 1;//3
if (a == 0) {//4
// 进入临界区域
b = 0;
// 退出临界区域
}else {
// 执行其他操作
}
分析:
如果process 1先执行if(b==0)
或者process 2先执行if (a==0)
,即存在一个进程的执行顺序和定义顺序不一致(可能是由于编译器或者处理器的优化措施导致),就有可能导致两个进程同时进入临界区域,从出现错误。因此必须要求:从单个进程来看,每个处理器的执行顺序必须和定义顺序一致,从而就禁止了编译器或者处理器的优化措施。
从上述要求1,可以得出下面两个有用的推论:
- 从全局的角度,每个CPU看到的其他CPU的写操作都是按照相同的顺序执行的,看到的最终执行的视图是一致的;
- 单个CPU对共享变量的写操作马上对其他CPU可见
证明:假设单个CPU对共享变量的写操作不是马上对其他CPU可见的,即在当前线程把写过的数据缓存在本地内存中,且还没有刷新到主内存之前,这个写操作仅对当前线程可见;从其他线程的角度来观察,会认为这个写操作根本还没有被当前线程执行。只有当前线程把本地内存中写过的数据刷新到主内存之后,这个写操作才能对其他线程可见。在这种情况下,当前线程和其它线程看到的操作执行顺序将不一致,从而违反推论1。
【案例2】假设每个内存模块都有多个端口,一个端口服务一个处理器。假设a和b被存储在不同的内存模块中,现在发生了以下事件:
处理器1向内存块1上专用于服务处理器1的端口发送
a=1
的请求。但是内存块1正忙于执行其他处理器的操作;处理器1向内存块2上专用于服务处理器1的端口发送
fetch b
的请求。内存2是空闲的,则开始执行,进入临界区;处理器2向内存块2上专用于服务处理器2的端口发送
b=1
的请求。这个请求在处理器1的fetch b
完成后开始执行;4)处理器2向内存块1上专用于服务处理器2的端口发送
fetch a
的请求。此时内存块1还忙于执行其他处理器的操作;
现在有两个操作等着被内存块2执行,如果处理器2的fetch a
请求先被执行,则同时有两个进程进入临界区域。进而有第二个要求:
单个内存块必须按照先来先服务的规则来处理来自多个处理器的所有请求。
- 来自Java1.7语言规范里的[https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5](Java Memory Model)里的定义:
A set of actions is sequentially consistent if all actions occur in a total order (the execution order) that is consistent with program order, and furthermore, each read r of a variable v sees the value written by the write w to v such that:
- w comes before r in the execution order, and
- there is no other write w' such that w comes before w' and w' comes before r in the execution order.
Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread.
If a program has no data races, then all executions of the program will appear to be sequentially consistent.
Sequential consistency and/or freedom from data races still allows errors arising from groups of operations that need to be perceived atomically and are not.
If we were to use sequential consistency as our memory model, many of the compiler and processor optimizations that we have discussed would be illegal.
【参考资料】
- https://www.microsoft.com/en-us/research/wp-content/uploads/2016/12/How-to-Make-a-Multiprocessor-Computer-That-Correctly-Executes-Multiprocess-Programs.pdf
- 《聊聊高并发(三十三)Java内存模型那些事(一)从一致性(Consistency)的角度理解Java内存模型》http://blog.csdn.net/iter_zc/article/details/41943387
- 《深入理解Java内存模型(三)——顺序一致性》http://www.infoq.com/cn/articles/java-memory-model-3
- 《为什么程序员需要关心顺序一致性(Sequential Consistency)而不是Cache一致性(Cache Coherence?)》
http://www.infoq.com/cn/articles/java-memory-model-3