函数范围静态变量中出现死锁(VC ++中的线程不安全)

时间:2010-02-26 06:42:54

标签: c++ static deadlock

问题是在多个线程上调用函数时如何构造函数级静态?

问题描述:发生死锁,我的应用程序没有被终止。在初始化本地静态变量期间,它尝试获取MSVCR80!_lock并且永远不会保持锁定。

WinDbg中的

!locks命令提供以下输出。

CritSec ntdll!LdrpLoaderLock + 0 at 7c97e178
LockCount 0

RecursionCount 1

OwningThread 1998

EntryCount d

ContentionCount d

***已锁定

CritSec MSVCR80!__ app_type + 94 at 781c3bc8

LockCount 1

RecursionCount 1

OwningThread 9a8

EntryCount 1

ContentionCount 1

***已锁定

下面是调用堆栈,您将看到它永远不会被锁定_mlock

**

  

调用堆栈线程17e8

**

781c3bc8 78132bd9 0777fde4 ntdll!RtlEnterCriticalSection + 0x46

00000008 b87d2630 00000000 MSVCR80!_lock + 0x2e

0864ae10 08631d7f 0864ae10 EPComUtilities32!_onexit + 0x36

0864ae10 b87d2588 00000001 EPComUtilities32!atexit + 0x9

0777fea8 0864719f 08630000 EPComUtilities32!XCriticalSectionEx :: ThreadTerminated + 0x5f

08630000 00000003 00000000 EPComUtilities32!DllMain + 0x20

08630000 7c90118a 08630000 EPComUtilities32!__ DllMainCRTStartup + 0x7a

08630000 00000003 00000000 EPComUtilities32!_DllMainCRTStartup + 0x1d #

**

  

调用堆栈线程1100

**

000000b0 00000000 00000000 ntdll!ZwWaitForSingleObject + 0xc

000000b0 ffffffff 00000000 kernel32!WaitForSingleObjectEx + 0xa8

000000b0 ffffffff 06ce64e0 kernel32!WaitForSingleObject + 0x12

000480ba 000f4240 00000000 CATSysMultiThreading!CATThreads :: Join + 0xf5

0012fcc8 00000004 00000000 JS0GROUP!CATLM :: StopHB + 0xf4

d138509f 00416694 00000001 JS0GROUP!CATLM :: Unmake + 0x6b

00000000 00000000 00000000 MSVCR80!_cinit + 0xd6

00000000 0012fd6c 081e68d9 MSVCR80!退出+ 0xd

00000000 06d404f0 0998fb90 JS0GROUP!CATExit + 0x1d

<00> 00000000 004ef366 0000000d DNBPLMProvider!DNBEPLMTransactionMgt :: OnApplicationExit + 0x229

<00> 00000000 0012fd9c 004eabfc JS0GROUP!CATCallExits + 0x2bc

<00> 00000000 0012ff7c 0040cefd JS0GROUP!CATErrorNormalEnd + 0x31 00000000 06ce71d0 06ce71d0 JS0GROUP!CATExit + 0xc

00000007 06cdb120 059b61d8 DLMMfgContextSolver!main + 0x146d

ffffffff ffffffff bffde000 DLMMfgContextSolver!__ tmainCRTStartup + 0x10f

// Code snippet below 
void main() 
{

    atexit(MyCallBack); 
    exit(0); 

}

void MyCallBack() 
{

// Waitingforsingleobject() // Waits until all threads are terminated

}

EXE使用DLL_THREAD_DETACH标志调用DllMain,我们有一个明确的处理,如下所示

BOOL APIENTRY DllMain( HANDLE, DWORD dwReason, LPVOID ) 
{
  if(dwReason == DLL_THREAD_DETACH) 
  { 
    F1();
    F2();
  }
}

F1()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

F2()
{

    const static CComBSTR bstrMethod = __ FUNCTION __ ;

}

在函数中进行本地静态初始化是否安全。另外我注意到,如果静态变量在主应用程序的exit()之前被初始化,我没有看到任何问题。任何人都可以解释可能存在的问题吗?

注意:但是当我将静态变量设置为非静态时,不会发生死锁并且问题已解决。

另请告诉我任何可能有助于此情况的替代解决方案 急切等待回复。

3 个答案:

答案 0 :(得分:1)

从某种意义上说,弗拉德对你的问题的评论已经回答了这个问题:在DllMain中不要做任何事情(如果可能的话)。

你真的受到DllMain中明智的限制,任何涉及钻研另一个Dll(包括使用malloc等)的东西都已经出局了。事实上,您可以做的唯一事情是:调用Kernel32.dll(除了加载/卸载Dlls!)和初始化简单数据类型。

我真的认为,鉴于您对问题的描述,使用CComBSTR会破坏这些规则中的一个或多个。

希望这有帮助,

P.S。

来自MSDN博客的额外链接: http://blogs.msdn.com/oleglv/default.aspx

答案 1 :(得分:0)

你可以在第103行的C:\Program Files\Microsoft Visual Studio 8\VC\crt\src\atonexit.c中设置一个断点,并记录哪些线程以什么顺序获取_EXIT_LOCK1(以及每种情况下调用堆栈的内容,以及可能已经获取的其他锁定时间)?

此外,在您的示例代码中Waitingforsingleobject已被注释掉 - 您是否可以确认在发生死锁时代码中确实已注释掉了?

答案 2 :(得分:0)

虽然我不熟悉CComBSTR类,但我会对它进行一次尝试。

基本上,您有一个单独的对象(在这种情况下由多个线程构造),没有并发保护。这听起来不像坏事吗?

有很多文章说明为什么依赖于静态变量的自动初始化是一个坏主意。这就是你在这里所做的事情,事实上它们是作用于功能的,而不是模块真的没关系。