std :: thread导致DLLMain中的死锁 [英] std::thread cause deadlock in DLLMain

查看:496
本文介绍了std :: thread导致DLLMain中的死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,这是我说的:std是复杂的。



在VS2013这个简单的程序将导致死锁。

  #include< thread> 
#include< windows.h>

void foo()
{
}

void initialize()
{
std :: thread t );
}

BOOL APIENTRY DllMain(HMODULE,DWORD reason,LPVOID)
{
switch(reason)
{
case DLL_PROCESS_ATTACH:
initialize();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
返回TRUE;
}

在DLLMain中创建一个线程完全错了?这不是真的。从
Microsoft的Microsoft的最佳实践:
如果你不与其他
线程同步,创建一个线程可以工作。所以CreateThread工程,_beginthreadex工程,和
boost :: thread工作,但std ::线程将不工作。这是
调用堆栈:

  ntdll.dll!_NtWaitForSingleObject@12()
KernelBase.dll !_WaitForSingleObjectEx @ 12()
msvcr120d.dll!Concurrency :: details :: ExternalContextBase :: Block()行151
msvcr120d.dll!Concurrency :: Context :: Block()Line 63
msvcr120d.dll!Concurrency :: details :: _ Condition_variable :: wait(Concurrency :: critical_section& _Lck)行595
msvcp120d.dll!do_wait(_Cnd_internal_imp_t * * cond,_Mtx_internal_imp_t * * mtx,const xtime * target )Line 54
msvcp120d.dll!_Cnd_wait(_Cnd_internal_imp_t * * cond,_Mtx_internal_imp_t * * mtx)第81行
msvcp120d.dll!std :: _ Cnd_waitX(_Cnd_internal_imp_t * * _Cnd,_Mtx_internal_imp_t * * _Mtx)第93行
msvcp120d.dll!std :: _ Pad :: _ Launch(_Thrd_imp_t * _Thr)Line 73
mod.dll!std :: _ Launch< std :: _ Bind< 1,void,void(__cdecl * const) (void)> >(_ Thrd_imp_t * _Thr,std :: _ Bind< 1,void,void(__cdecl * const)(void)&& _Tg)206行
mod.dll!std :: thread :: thread ; void(__cdecl&)(void)>(void(void)* _Fx)第49行
mod.dll!initialize()第17行
mod.dll!DllMain(HINSTANCE__ * __formal,unsigned long原因,void * __formal)第33行

好吧,std :: thread会与其他线程同步



但是为什么?



我希望这在VS2015中不再发生,我还没有测试它。

解决方案

std :: thread 的规范包含以下要求(N4527§30.3.1.2[thread.thread.constr] / 6):


同步的构造函数的调用与 f 的副本的调用的开始同步。


f 是要在新创建的线程上执行的可调用实体。)



std :: thread 的构造函数不能返回,直到新线程开始执行线程过程。当一个新的线程被创建时,在线程过程被调用之前,每个被加载的DLL的入口点被调用 DLL_THREAD_ATTACH 。为此,新线程必须获取加载器锁。不幸的是,你现有的线程已经拥有加载器锁。



因此,你死锁:现有线程不能释放加载器锁,直到新线程开始执行线程程序,但是新线程不能执行线程过程,直到它可以获取由现有线程持有的加载器锁。



请注意,文档明确建议不要从DLL入口点创建线程:

$您不应该从 DllMain 中执行以下任务:[...]调用<$ c <$ c $ b $ c> CreateThread 。如果你不与其他线程同步,创建线程可以工作,但它是有风险的。


不应该从DLL入口点执行的事情;这只是其中的一个。)


So, this is what I'm talking about: std is complex.

In VS2013 this simple program will cause a deadlock.

#include <thread>
#include <windows.h>

void foo()
{
}

void initialize()
{
    std::thread t(foo);
}

BOOL APIENTRY DllMain(HMODULE, DWORD reason, LPVOID)
{
    switch (reason)
    {
    case DLL_PROCESS_ATTACH:
        initialize();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Create a thread in DLLMain is totally wrong ? It's not true. From the document "Best Practices for Creating DLLs" of Microsoft: "Creating a thread can work if you do not synchronize with other threads". So CreateThread works, _beginthreadex works, and boost::thread works, but std::thread will not work. This is the call stack:

ntdll.dll!_NtWaitForSingleObject@12()
KernelBase.dll!_WaitForSingleObjectEx@12()
msvcr120d.dll!Concurrency::details::ExternalContextBase::Block() Line 151
msvcr120d.dll!Concurrency::Context::Block() Line 63
msvcr120d.dll!Concurrency::details::_Condition_variable::wait(Concurrency::critical_section & _Lck) Line 595
msvcp120d.dll!do_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx, const xtime * target) Line 54
msvcp120d.dll!_Cnd_wait(_Cnd_internal_imp_t * * cond, _Mtx_internal_imp_t * * mtx) Line 81
msvcp120d.dll!std::_Cnd_waitX(_Cnd_internal_imp_t * * _Cnd, _Mtx_internal_imp_t * * _Mtx) Line 93
msvcp120d.dll!std::_Pad::_Launch(_Thrd_imp_t * _Thr) Line 73
mod.dll!std::_Launch<std::_Bind<1,void,void (__cdecl*const)(void)> >(_Thrd_imp_t * _Thr, std::_Bind<1,void,void (__cdecl*const)(void)> && _Tg) Line 206
mod.dll!std::thread::thread<void (__cdecl&)(void)>(void (void) * _Fx) Line 49
mod.dll!initialize() Line 17
mod.dll!DllMain(HINSTANCE__ * __formal, unsigned long reason, void * __formal) Line 33

Okay, std::thread will "synchronize with other threads".

But why ?

I hope this never happens again in VS2015, I didn't test it yet.

解决方案

The specification for std::thread contains the following requirement (N4527 §30.3.1.2[thread.thread.constr]/6):

Synchronization: The completion of the invocation of the constructor synchronizes with the beginning of the invocation of the copy of f.

(where f is the callable entity which is to be executed on the newly created thread.)

The constructor for the std::thread cannot return until the new thread starts executing the thread procedure. When a new thread is created, before the thread procedure is invoked, the entry point of each loaded DLL is invoked for DLL_THREAD_ATTACH. To do this, the new thread must acquire the loader lock. Unfortunately, your existing thread already holds the loader lock.

Thus, you deadlock: the existing thread cannot release the loader lock until the new thread starts executing the thread procedure but the new thread cannot execute the thread procedure until it can acquire the loader lock, which is held by the existing thread.

Note that the documentation expressly recommends against creation of threads from the DLL entry point:

You should never perform the following tasks from within DllMain: [...] Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky.

(That page has a long list of things that should not be done from a DLL entry point; this is just one of them.)

这篇关于std :: thread导致DLLMain中的死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆