当从DllMain()锁定时,Visual Studio 2012死锁中的C++11 std::mutex
C++11 std::mutex in Visual Studio 2012 deadlock when locked from DllMain()
当互斥锁从DllMain()
锁定时,我看到std::mutex
出现死锁。下面是一个最小的DLL测试用例,它向我展示了这个问题。我的实际代码执行互斥锁,因为它使用的成员函数在正常函数期间也可以在初始化之外使用。
我认为问题在于main()
线程的调用堆栈中的调度器与调度器派生的其他线程(可能)之间的死锁。死锁似乎发生在main()
实际执行之前。
对于如何解决僵局,我将不胜感激。
简单DLL:
static void testFunc()
{
std::mutex mtx;
mtx.lock();
mtx.unlock();
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
testFunc ();
break;
case DLL_THREAD_ATTACH:
testFunc ();
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
在死锁点,进程中有两个线程:
Not Flagged > 6408 0 Main Thread Main Thread msvcr110d.dll!Concurrency::details::SchedulerBase::SchedulerBase Normal
Not Flagged 7600 0 Worker Thread ntdll.dll!_TppWaiterpThread@4() ntdll.dll!_NtDelayExecution@8 Normal
以下是main()
线程的调用堆栈:
ntdll.dll!_NtWaitForKeyedEvent@16() Unknown
ntdll.dll!_TppWaitpSet@16() Unknown
ntdll.dll!_TppSetWaitInterrupt@12() Unknown
ntdll.dll!_RtlRegisterWait@24() Unknown
kernel32.dll!_RegisterWaitForSingleObject@24() Unknown
> msvcr110d.dll!Concurrency::details::SchedulerBase::SchedulerBase(const Concurrency::SchedulerPolicy & policy) Line 152 C++
msvcr110d.dll!Concurrency::details::ThreadScheduler::ThreadScheduler(const Concurrency::SchedulerPolicy & policy) Line 26 C++
msvcr110d.dll!Concurrency::details::ThreadScheduler::Create(const Concurrency::SchedulerPolicy & policy) Line 34 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CreateWithoutInitializing(const Concurrency::SchedulerPolicy & policy) Line 276 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::GetDefaultScheduler() Line 650 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 567 C++
msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 399 C++
msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
msvcr110d.dll!Concurrency::critical_section::lock() Line 1017 C++
msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65 C++
msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144 C++
ConsoleApplicationDll.dll!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68 C++
ConsoleApplicationDll.dll!std::_Mutex_base::lock() Line 43 C++
ConsoleApplicationDll.dll!testFunc() Line 16 C++
ConsoleApplicationDll.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 29 C++
ConsoleApplicationDll.dll!__DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 508 C
ConsoleApplicationDll.dll!_DllMainCRTStartup(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 472 C
ntdll.dll!_LdrpCallInitRoutine@16() Unknown
ntdll.dll!_LdrpRunInitializeRoutines@4() Unknown
ntdll.dll!_LdrpInitializeProcess@8() Unknown
ntdll.dll!__LdrpInitialize@8() Unknown
ntdll.dll!_LdrInitializeThunk@8() Unknown
第二个线程的调用堆栈很短:
> ntdll.dll!_NtDelayExecution@8() Unknown
ntdll.dll!__LdrpInitialize@8() Unknown
ntdll.dll!_LdrInitializeThunk@8() Unknown
编辑1:
WinDbg确认是加载程序锁定问题:
PRIMARY_PROBLEM_CLASS: APPLICATION_HANG_HungIn_LoaderLock
查看创建DLL的最佳实践文档:
您永远不应该在DllMain中执行以下任务:
- 调用LoadLibrary或LoadLibraryEx(直接或间接)。这可能会导致死锁或崩溃
- 与其他线程同步。这可能会导致死锁
使用QueueUserAPC()
排队初始化似乎总是在main()之前执行,但在可怕的加载程序锁之外。这看起来像是解决了我的问题。
编辑1
经过一些测试,如果我从DllMain()
对APC进行排队,APC方法似乎有效,但如果我从类的静态全局实例的ctor对APC进行队列,则它不起作用。IOW,对我来说,使用APC并不能在编译器和构建模式的所有可能组合中统一使用。
相关文章:
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何在没有死锁和/或争用的情况下正确使用 std::mutex C++?
- 用C++中的std::condition_variable将线程置于死锁中会有风险吗
- 使用 std::async 时死锁,将来作为成员
- 如何调试读写器锁的死锁?
- 为什么在Visual Studio 2013上的std::this_thread::sleep_for上死锁
- localtime() 函数正在调用 ___lll_lock_wait_private(),这会使线程陷入死锁
- 如何重现 Boost 进程文档提示的死锁?
- 多线程Windows GUI应用程序中的死锁
- 为什么printf会导致与future.get的死锁,而cout则不会?
- C++中具有阻塞队列和障碍的死锁
- 死锁使用 std::mutex 来保护多个线程中的 cout
- 避免并发等待对象中的死锁
- 在VC++中从DLLMAIN内部调用D3D的CREATEDEVICE时,它会创建一个死锁(loaderlock?)。有没有办法克服这个问题?最终目标内
- 当用2个螺纹锁定时,将recursive_mutex死锁
- 程序在 C++11 中使用条件变量进入死锁
- 一个线程提升的死锁
- 线程安全std::cout的死锁
- C++:std::async 和 std::mutex 会导致 Linux 上的死锁,但在 Windows 上运行
- 当从DllMain()锁定时,Visual Studio 2012死锁中的C++11 std::mutex