问题是在多个线程上调用函数时如何构造函数级静态?
问题描述:发生死锁,我的应用程序没有被终止。在初始化本地静态变量期间,它尝试获取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 + 0xc00000007 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()之前被初始化,我没有看到任何问题。任何人都可以解释可能存在的问题吗?
注意:但是当我将静态变量设置为非静态时,不会发生死锁并且问题已解决。
另请告诉我任何可能有助于此情况的替代解决方案 急切等待回复。
答案 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类,但我会对它进行一次尝试。
基本上,您有一个单独的对象(在这种情况下由多个线程构造),没有并发保护。这听起来不像坏事吗?
有很多文章说明为什么依赖于静态变量的自动初始化是一个坏主意。这就是你在这里所做的事情,事实上它们是作用于功能的,而不是模块真的没关系。