当从 DllMain() 锁定时,Visual Studio 2012 中的 C++11 std::mutex 会死锁

C++11 std::mutex in Visual Studio 2012 deadlock when locked from DllMain()(当从 DllMain() 锁定时,Visual Studio 2012 中的 C++11 std::mutex 会死锁)

本文介绍了当从 DllMain() 锁定时,Visual Studio 2012 中的 C++11 std::mutex 会死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当互斥锁从 DllMain() 锁定时,我看到 std::mutex 出现死锁 下面是一个最小的 DLL 测试用例,它向我展示了这个问题.我的实际代码执行互斥锁,因为它使用的成员函数在正常函数期间也可在初始化之外使用.

I am seeing a deadlock with std::mutex when the mutex is locked from DllMain() Below is a minimal DLL test case that exhibits the problem for me. My actual code does the mutex locking because it uses member functions that are also usable outside initialization during normal function.

我认为问题是调度程序之间的死锁,如 main() 线程的调用堆栈和调度程序产生的另一个线程(可能)所见.死锁似乎发生在 main() 实际执行之前.

I think that the problem is a deadlock between the scheduler as seen in the call stack of main() thread and the other thread (probably) spawned by the scheduler. The deadlock seems to happen before main() is actually executed.

对于如何修复/解决死锁的任何建议,我将不胜感激.

I would appreciate any advice as to how to fix/resolve the deadlock.

简单的 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;
}

在死锁点,进程中有两个线程:

At the point of the deadlock there are two threads in the process:

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

第二个线程的调用栈很短:

The second thread's call stack is short:

>   ntdll.dll!_NtDelayExecution@8() Unknown
    ntdll.dll!__LdrpInitialize@8()  Unknown
    ntdll.dll!_LdrInitializeThunk@8()   Unknown

编辑 1:

WinDbg 确认是加载程序锁定问题:

WinDbg confirms that it is loader lock issue:

PRIMARY_PROBLEM_CLASS:  APPLICATION_HANG_HungIn_LoaderLock

推荐答案

似乎使用 QueueUserAPC() 进行队列初始化总是在 main() 之前执行,但在可怕的加载器锁之外.这看起来像是我的问题的解决方案.

It seems that using QueueUserAPC() to queue initialization is always executed before main() but out of the dreaded loader lock. This looks like a solution to my problem.

编辑 1

经过一些测试,如果我将来自 DllMain() 的 APC 排队,APC 方法似乎有效,但如果我将 APC 从一个类的静态全局实例的 ctor 排队,则它不起作用.IOW,对我来说,在编译器和构建模式的所有可能组合中使用 APC 并不是统一可用的.

After some testing it seems that the APC method works if I queue the APC from DllMain() but it does not work if I queue the APC from a ctor of a static global instance of a class. IOW, using the APC is not uniformly usable across all possible combinations of compilers and build modes for me.

这篇关于当从 DllMain() 锁定时,Visual Studio 2012 中的 C++11 std::mutex 会死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:当从 DllMain() 锁定时,Visual Studio 2012 中的 C++11 std::mutex 会死锁

基础教程推荐