(无效方式)避免C#中的双重检查锁

时间:2011-04-28 16:29:33

标签: c# multithreading locking

这是避免双重检查锁的有效且优化的方法:

public class SomeBaseClass
{
     protected static object InitializeLock = new object();
     protected static bool IsInitialized = false;

     public void SomeFunction()
     {
         if (!IsInitialized)
         {
             System.Threading.Thread.MemoryBarrier();
             lock (InitializeLock)
             {
                 // do init stuff
                 IsInitialized = true;
             }
     }

     //Do stuff that have to happen when function is called
    }
}

这是双重检查的选择:

public class SomeBaseClass
{
     protected static object InitializeLock = new object();
     protected static bool IsInitialized = false;

     public void SomeFunction()
     {
         if (!IsInitialized)
         {
             lock (InitializeLock)
             {
                 if (!IsInitialized)
                 {                              
                     // do init stuff
                     IsInitialized = true;
                 }
             }
         }

     //Do stuff that have to happen when function is called
    }
}

4 个答案:

答案 0 :(得分:6)

不,因为线程切换可能在两个线程通过if (!IsInitialized)

之后发生

有一篇很棒的文章,在创建单身人士的背景下解释了这个主题:http://csharpindepth.com/Articles/General/Singleton.aspx(作者Jon Skeet)

答案 1 :(得分:5)

这是今天第二次出现这个问题。参见:

C# manual lock/unlock

对你的问题的简短回答是否定的,这绝对无效。如果针对正在初始化的任何状态的非易失性读取重新排序“IsInitialized”的非易失性读取,那么代码路径上的任何类型的都不会有内存屏障,因此读取可以重新排序,因此“IsInitialized”可以为真,而过时的缓存未初始化状态仍然良好。

你需要做的是(1)不要进行双重检查锁定;这是危险的,或(2)确保始终至少有一个易读的IsInitialized,以防止读取初始化状态及时向后移动。

答案 2 :(得分:4)

第一个示例中的MemoryBarrier调用完全是多余的,因为后续的lock调用无论如何都会创建隐式内存屏障。

即使您在第一次IsInitialized检查之前移动了内存屏障,代码仍然不安全:在IsInitialized检查和lock之间有一个窗口可以中断线程声明。这就是为什么您通常需要在IsInitialized块内进行第二次lock检查。

答案 3 :(得分:0)

您可以通过IsInitialized标记volatile帮助检查,这会阻止其他线程缓存它(自锁定以来这是一个非常小的改进),但是你锁定后仍然需要旗帜。换句话说,除非你使用一些棘手的初始化,否则你无法避免双重检查锁。

然而,如果你重新设计你的课程,你可以取消锁定,如果你采取乐观的方法改变状态......这应该像魅力一样:

public class Internals
{
    private readonly bool IsInitialized;

    public Internals(bool initialized)
    {
        IsInitialized = initialized;
    }
}


public class SomeBaseClass
{        
    protected static Internals internals = new Internals(false);

    public void SomeFunction()
    {
        do
        {
            Internals previous = internals;
        }while(!previous.IsInitialized && previous != Interlocked.CompareExchange(internals, new Internals(true), previous))
    }
}