首先说下,研究内存模型是上瘾的,其涵盖了硬件层次,编译器原理,并行处理技术,计算机原理和数学.
在多线程编程中,死锁往往是令人头痛的,如以下代码,先人工编译下,再机器编译下,看看你有没有猜对?
class Test
{
private bool _loop = true;
public static void Main()
{
Test test1 = new Test();
// 在另一个线程中,设置该标志为false
new Thread(() => { test1._loop = false;}).Start();
while (test1._loop == true) ;
}
}
通常有两种办法解决并发 冲 突,1,众所周知的lock;2,为_loop变量加上volatile前缀.
事实上,以上代码,经过CLR JIT编译,while (test1._loop == true) ;会"优化"为if (test1._loop) { while (true); }
对编译器来说,是合情合理的,也可以看出,_loop一旦被另一个线程修改,不会对本线程的流程造成任何影响.
JIT将会把 test1._loop 存放到CPU寄存器EAX中,然后ASM会变成如下
00000068 test eax,eax
0000006a jne 00000068
如果我们把_loop前缀标志为volatile将得到变化后的ASM大概是这样
00000064 cmp byte ptr [eax+4],0
00000068 jne 00000064
这样编译器就会不停的把数据从内存加载到EAX中,再进行比较.
=======以上是在X86处理器架构上的分析=========
在某些系统架构上(除了x86,x64,还有IA64等)情况又会有不同,特别是在多核盛行的当今社会,在并行处理需求更多的如今,服务器环境变的越来越复杂.
抛开这个题外话,我们继续深入内存模型.通过研究,得出如下模型(主要是"读"和"写"的区别):
操作-----------执行之前cache更新否?------------操作之后cache更新否?--------------备注
原始读 0 0
原始写 0 1
V读 1 0
V写 0 1
Thread.
MemoryBarrier 1 1
Interlocked 1 1
Lock 上锁边界 1 0 Monitor.Enter()同理
Lock 取锁边界 0 1 Monitor.Enter()同理