好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

双检锁技术

双检锁技术

最近公司的项目中发现一个编译优化导致的bug。同事叙述为 “ 在 CPU 开启 out-of-order execution 优化时,是有 bug 的”。 针对这个问题,比较好的优化方法如下:

?

private   static   JobManager self;

private   static   object   asyncObj = new   object ();

 

public   static   JobManager Instance

{

     get

     {

         if   (self == null )

         {

             lock   (asyncObj)

             {

                 if   (self == null )

                 {

                     // 正确的实现方法应该为: var temp = new JobManager(); Interlocked.Exchange(ref self, temp);

                     self = new   JobManager();

                 }

             }

         }

         return   self;

     }

}

这里需要解释一下:

self = new JobManager()

这句你的本意是为 JobManager 分配内存,调用构造器初始化字段,再将引用赋给 self ,即 发布 出来让其他线程可见。但是,那只是你一厢情愿的想法,编译器可能这样做:为JobManager 分配内存,将引用发布到(赋给)self,再调用构造器。然而,如果在将引用发布给 self 之后,调用构造器之前,另一个线程发现 self 不为 null,便开始使用JobManager对象,这时会发生什么?这个时候对象的构造器还没有执行结束!这是一个很难追踪的bug。

从双检锁技术的角度来看,使用 Interlocked.Exchange 确实是最好的解决方案。但有两个问题,它该如何解决?

1.速度是否够快?

2.如果一个线程池线程在Monitor的线程同步构造上阻塞,线程池会创建另一个线程来保持CPU的“饱和”,而创建一个新线程的代价是很昂贵的,我们该如何避免这样的情况?

 

试着跳出“lock+2次if”的框子,我们可以使用 Interlocked.CompareExchange 来解决上面的问题。下面是一个示例:

?

internal   sealed   class   MySingleton

{

     private   static   MySingleton s_value = null ;

     public   static   MySingleton GetMySingleton()

     {

         if   (s_value != null ) return   s_value;

 

         MySingleton temp = new   MySingleton();

         Interlocked.CompareExchange( ref   s_value, temp, null );

         return   s_value;

     }

}

虽然多个线程同时调用GetMySingleton,会创建2个或者更多的MySingleton对象,但没有被s_value引用的临时对象会在以后被垃圾回收。大多数应用程序很少会发生同时调用GetMySingleton的情况,所以不太可能出现创建多个MySingleton对象的情况。上述代码带来优势是很明显的,首先,它的速度是非常快,其次,它永不阻塞线程。这就解决了前面在双检锁技术中提出的问题。

 

另外,在.net 4.0中提供了2个类型封装上述两种模式(双检锁技术、使用 Interlocked.CompareExchange 技术):

泛型 System.Lazy 类和 System.Threading.LazyInitializer 类。下面是2个示例:

?

public   static   void   Main()

{

     Lazy< string > s = new   Lazy< string >(() => DateTime.Now.ToLongTimeString(), LazyThreadSafetyMode.PublicationOnly);

 

     Console.WriteLine(s.IsValueCreated);

     Console.WriteLine(s.Value);

     Console.WriteLine(s.IsValueCreated);

     Thread.Sleep(5000);

     Console.WriteLine(s.Value);

     Console.WriteLine(DateTime.Now.ToLongTimeString());

}

 

输出结果:

?

public   static   void   Main()

{

     string   name = null ;

 

     LazyInitializer.EnsureInitialized( ref   name, () => "Benjamin" );

     Console.WriteLine(name);

 

     LazyInitializer.EnsureInitialized( ref   name, () => "Yao" );

     Console.WriteLine(name);

}

输出结果:

其中枚举 LazyThreadSafetyMode 解释如下:

?

public   enum   LazyThreadSafetyMode

{

     None = 0,     //完全没有线程安全劫持(适合GUI应用程序)

     PublicationOnly = 1,      //使用Interlocked.CompareExchange技术

     ExecutionAndPublication = 2,     //使用双检锁技术

}


转载需注明
转载自: 博客园 - 爱拼才会赢 -  benjamin超人   http://HdhCmsTestcnblogs测试数据/BenjaminYao/

分类:  .Net技术

标签:  双检锁技术 ,  多线程 ,  Interlocked.Exchange ,  Interlocked.CompareExchange ,  CPU out-of-order execution ,  System.Lazy ,  System.Threading.LazyInitializer

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于双检锁技术的详细内容...

  阅读:43次

上一篇: MSBuild

下一篇:mvc项目