双重检查锁定不是线程安全的?

时间:2015-07-10 17:43:30

标签: vb.net multithreading memory thread-safety locking

当我使用双重检查锁定来创建一个线程安全的单例时,我在WPF应用程序中经历了一个非常奇怪的行为。我的代码代表了该技术的通常实现:

  Private _graphics As Contracts.Interfaces.Components.IGraphics
    ReadOnly Property Graphics As Contracts.Interfaces.Components.IGraphics
        Get
            If _graphics Is Nothing Then
                SyncLock lock
                    If (_graphics Is Nothing) Then
                        _graphics = New GFX.Application()
                    End If
                End SyncLock
            End If
            Return _graphics
        End Get
    End Property

当我运行程序时,我遇到了异常,因为Application-object构造了两次。我无法相信自己的眼睛,因为我对锁的工作方式的理解告诉我这应该是线程安全的。

我的假设是,这段代码在同一个线程上运行了两次(异步),所以我最后修改了我的代码来分析这个问题。每次线程进入锁定语句时,我都会添加一个List以便将Thread-object放入其中。接下来,我只需要将包含的Thread-object与当前线程进行比较:

ReadOnly Property Graphics As Contracts.Interfaces.Components.IGraphics
    Get
        If _graphics Is Nothing Then
            SyncLock lock
                If (_graphics Is Nothing) Then
                    If (threadList.Any() AndAlso (threadList.First() IsNot Threading.Thread.CurrentThread)) Then
                        Stop ' Program is stopping here.
                    End If
                    threadList.Add(Threading.Thread.CurrentThread)
                    _graphics = New GFX.Application() ' Object construction takes around 70 ms.
                End If
            End SyncLock
        End If
        Return _graphics
    End Get
End Property

正如您可能猜到的,程序停止了,因为多个线程遇到了相同的锁定语句。这怎么可能是真的?我对这种行为绝对没有解释,请告诉我你是否有任何想法。感谢。

更新: 我想补充一些细节。这是初始化例程,导致两个匿名方法异步运行。他们每个人都访问Singleton持有人的只读属性。

 Task.Factory.StartNew(Sub() Graphics.Init(...)
 Task.Factory.StartNew(Sub() Graphics.Run(...)

以下是相关的反汇编指令。也许他们给出了一个指示。这里有人能够遵循这些指示:

 If _graphics Is Nothing Then
002D91ED  cmp         dword ptr ds:[38E3430h],0  
002D91F4  sete        al  
002D91F7  movzx       eax,al  
002D91FA  mov         dword ptr [ebp-48h],eax  
002D91FD  cmp         dword ptr [ebp-48h],0  
002D9201  je          002D9297  
                SyncLock lock
002D9207  nop  
002D9208  mov         eax,dword ptr ds:[038E3438h]  
002D920D  mov         dword ptr [ebp-40h],eax  
002D9210  mov         ecx,dword ptr [ebp-40h]  
002D9213  call        518DCE8C  
002D9218  nop  
002D9219  xor         edx,edx  
002D921B  mov         dword ptr [ebp-44h],edx  
002D921E  nop  
002D921F  lea         edx,[ebp-44h]  
002D9222  mov         ecx,dword ptr [ebp-40h]  
002D9225  call        7301A2B0  
002D922A  nop  
                    If _graphics Is Nothing Then
002D922B  cmp         dword ptr ds:[38E3430h],0  
002D9232  sete        al  
002D9235  movzx       eax,al  
002D9238  mov         dword ptr [ebp-48h],eax  
002D923B  cmp         dword ptr [ebp-48h],0  
002D923F  je          002D9264  
                        _graphics = New GFX.Application()
002D9241  mov         ecx,47D664h  
002D9246  call        73E601D2  
002D924B  mov         dword ptr [ebp-4Ch],eax  
002D924E  mov         ecx,dword ptr [ebp-4Ch]  
002D9251  call        002D8C10  
002D9256  mov         eax,dword ptr [ebp-4Ch]  
002D9259  lea         edx,ds:[38E3430h]  
002D925F  call        73E52360  
                    End If
002D9264  nop  
                End SyncLock

0 个答案:

没有答案