多线程问题与double-check小结

一般地,多线程问题常常发生于对于共享资源的同时使用。例如,对于类的成员变量的使用,对于全局静态变量的使用,而对于函数内部的局部变量而言,一般式不会存在多线程问题的,因为每个线程在调用一个特定的函数时,都会生成一份函数内部成员变量的副本,线程和线程之间是互不相干的。
    解决多线程问题,最常见的方式就是加锁,使得某一资源在同一时刻只能被一个线程所用,而其他线程则必须在被加锁的代码外等待,直到锁被解除,例如如下c#代码所示:
lock(_lock) 
{
    //do something to the shared resources.
}
    下面说说double-check。
    多线程问题也常常和一种lazy-initialize的设计模式联系在一起。在这里就会慢慢引出double-check。lazy-initialize讲的是,对于一些特别复杂的对象,让程序在第一次调用它的时候再对它进行初始化,而且保证仅仅初始化一次。
    首先想到的设计是这样的:
Class A
{
    private static ComplexClass _result = null;
    public ComplexClass GetResult()
    {
         if(_result == null)
         {
              _result = new ComplexClass();
         }
         return _result;
    }
}
    但是这样有一个问题。ComplexClass的构造过程较长的话,当第一个线程还在进行ComplexClass构造的时候,_result可能是null,也可能指向了一个尚未初始化完成的对象。这样,要么两个线程初始化了两次ComplexClass,要么第二个线程会返回一个指向不完整对象的引用。所以,在这里需要用到一个锁,如下所示:
    ComplexClass GetResult()
    {
            lock(_lock)
        {
             if(_result == null)
             {
                  _result = new ComplexClass();
             }
         }         
         return _result;
    }
    这样,虽然多线程的问题解决了,但是每一次需要使用result时都会请求锁,而请求锁对程序的性能是有很大影响的,因此我们在lock的外面再加一层check:
    ComplexClass GetResult()
    {
            if(_result == null)
        {
            lock(_lock)
            {
                 if(_result == null)
                 {
                      _result = new ComplexClass();
                 }
             }  
         }       
         return _result;
    }
    这样,对于所有初始化完成后的请求,就都不用请求锁,而是直接返回_result。
    但是还是存在一点问题。对于一些编程语言来说,_result = new ComplexClass();这句代码会使得_result指向一个部分初始化的对象。也就是说,当线程A在初始化ComplexClass时,线程B有可能会判断_result已经不是null了,而这时其实初始化尚未完成,这时线程B就直接返回了一个部分初始化的对象,会造成程序的崩溃。那么,这个问题怎么解决呢?一般的解决方法是在程序内部再加一个局部变量(标识变量)做一层缓冲:
    ComplexClass GetResult()
    {
        ComplexClass result;
        if(_result == null)
        {
            lock(_lock)
            {
                 if(_result == null)
                 {
                      result = new ComplexClass();
                                 _result = result;
                 }
             }  
         }       
         return _result;
    }
这样,上面的问题就彻底解决了~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值