万分疑惑volatile这个关键字

无意中看到网上有个线程安全单例模式这样的写法:
 
    public sealed class Singleton 
  { 
      
private static volatile Singleton instance; 
      
private static object syncRoot = new Object(); 
      
private Singleton() {} 
      
public static Singleton Instance 
      { 
          
get 
          { 
              
if (instance == null
              { 
                  
lock (syncRoot) 
                  { 
                      
if (instance == null
                      instance 
= new Singleton(); 
                  } 
              } 
              
return instance; 
           } 
       } 
  }


    volatile? volatile ? 狂汗, 用了C#这么久,还是第一次看到,顿感惭愧无比。打开MSDN,查了一下解释:

The volatile keyword indicates that a field might be modified by multiple threads that are executing at the same time. Fields that are declared volatile are not subject to compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

The volatile modifier is usually used for a field that is accessed by multiple threads without using the lock statement to serialize access. 

    这样的解释未免太过简单,而且有些不负责任,无奈放狗google,找到一些解释实在值得商榷,普遍认同的说发是:编译器优化会将当前的field放入CPU cache中,这对单线程程序会有很大好处,但碰到多线程的情况下,因为当前的线程只会更改自己的Cache,由于不能及时更新到内存,会导致其它线程脏读,加上“volatile”关键字则告诉编译器不要做这样的Cache优化,这样就不会产生问题。

    其实这种说法是非常错误的,CPU Cache是CPU体系结构自己维护的,与内存之间也是的数据同步也是自动的,是不受编译器控制的(至少X86,X64处理器是这样),Cache什么完全是由CPU缓存预测算法自己决定,何来在ISA层次控制一说?(或许其他架构的处理器可以在ISA层控制,不过这样的处理器不用也罢),就我所知,无论是AMD X2这样的独立L2 Cache处理器,还是酷睿这样的共享L2 Cache处理器,都有一整套缓存策略用以维护缓存和内存数据的一致性,符合Cache预取条件的数据,CPU会自动将它维护在Cache中,更改了这个Cache,必会更改内存,如果是独立缓存体系,还会更改另外的Cache,这是个原子操作,不用也不能干预(除非你有本事拿激光改CPU电路)。

    看下面这段代码:

        

        
volatile bool IsStop;
        
//bool IsStop;



        
int Count;

        Thread th;
        
public Form1()
        {
            InitializeComponent();
            th 
= new Thread(new ThreadStart(A));
        }


        
private void button1_Click(object sender, EventArgs e)
        {
            th.Start();
        }


        
void A()
        {
            
while (!IsStop)
            {
                Thread.Sleep(
500);
                Count
++;
                
this.Invoke(new Func<int>(() =>
                    {
                        label1.Text 
= Count.ToString();
                        
return 0;
                    }));
            }
        }


        
private void button2_Click(object sender, EventArgs e)
        {
            
new Thread(() => IsStop = true).Start();
        }

        
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            
try
            {
                th.Abort();
            }
            
catch
            { }
        }


    无论是注释掉volatile bool IsStop;还是bool IsStop;其行为完全一致,点击button2就会使IsStop = true,th那个线程就会停掉,而且编译出的汇编代码完全一样(IL代码不一样),这让我很苦恼,“volatile”完全是个矛盾的,无须存在的东西。就像下面的代码,有没有关键词“volatile”没有任何区别(虽然编译出的IL代码不一样):

        int Int1;
        
int Int2;
        
volatile int Int3;
        
int Int4;
        
void b()
        {
            Int1
++;
00000000  mov         qword ptr [rsp+8],rcx 
00000005  sub         rsp,28h 
00000009  nop              
0000000a  mov         rax,642801A2E18h 
00000014  mov         eax,dword ptr [rax] 
00000016  test        eax,eax 
00000018  je          000000000000001F 
0000001a  call        FFFFFFFFFF7AA7B0 
0000001f  mov         rcx,qword ptr [rsp
+30h] 
00000024  mov         ecx,dword ptr [rcx+000001E4h] 
0000002a  add         ecx,
1 
0000002d  mov         rax,qword ptr [rsp
+30h] 
00000032  mov         dword ptr [rax+000001E4h],ecx 
            Int2
++;
00000038  mov         rcx,qword ptr [rsp+30h] 
0000003d  mov         ecx,dword ptr [rcx
+000001E8h] 
00000043  add         ecx,1 
00000046  mov         rax,qword ptr [rsp+30h] 
0000004b  mov         dword ptr [rax
+000001E8h],ecx 
            Int3
++;
00000051  mov         rcx,qword ptr [rsp+30h] 
00000056  mov         ecx,dword ptr [rcx+000001ECh] 
0000005c  add         ecx,
1 
0000005f  mov         rax,qword ptr [rsp
+30h] 
00000064  mov         dword ptr [rax+000001ECh],ecx 
            Int4
++;
0000006a  mov         rcx,qword ptr [rsp
+30h] 
0000006f  mov         ecx,dword ptr [rcx
+000001F0h] 
00000075  add         ecx,1 
00000078  mov         rax,qword ptr [rsp+30h] 
0000007d  mov         dword ptr [rax
+000001F0h],ecx 
        }
00000083  jmp         0000000000000085 
00000085  add         rsp,28h 
00000089  rep ret          


    Int2和Int3的行为无任何区别,那这个“volatile”还有什么意义?

    唯一能使我信服的解释就是:“volatile”这个关键词根本就不是为X86、X64这样的处理器准备的,可能是为了安藤这样的处理器吧,因为X86、X64就没有在ISA层提供外部操作Cache的接口,不过话说回来,如果哪种处理器需要手动控制Cache策略,那不如把它扔到垃圾堆里吧,你是在玩处理器还是在写商业代码?殊不知世界上指令性能最强大的处理器还不是酷睿,价格便宜量又足。如果不是需要处理大并发的浮点数据,不选酷睿或者AMD K8,K10实在是很有点吃力不讨好了,况且“volatile”一定程度上破坏了跨平台性,实在有点说不过去.........。

    谁有其他观点欢迎讨论,最好有理有据哦,呵呵。
posted @ 2008-06-25 13:33  王弈博  阅读(635)  评论(2编辑  收藏  举报