zoukankan      html  css  js  c++  java
  • 万分疑惑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”一定程度上破坏了跨平台性,实在有点说不过去.........。

        谁有其他观点欢迎讨论,最好有理有据哦,呵呵。
  • 相关阅读:
    ASP.NET 数据库访问通用工具
    [原]ASP.NET 数据库访问通用工具
    [转]序号的结构层次顺序
    序号的结构层次顺序
    百度地图显示多个标注点
    [原]百度地图显示多个标注点
    [原]网站跨站点脚本,Sql注入等攻击的处理
    网站跨站点脚本,Sql注入等攻击的处理
    angular学习笔记(十二)-控制器
    angular学习笔记(十一)-表达式
  • 原文地址:https://www.cnblogs.com/WYB/p/1229616.html
Copyright © 2011-2022 走看看