DllMain和多线程死锁 guke1978 1.doc
文本预览下载声明
DllMain和多线程死锁 guke1978 1
用户操作[留言][发消息][加为好友]guke1978_123的文章原创1篇翻译0篇转载0篇评论5篇订阅我的博客存档
2006年03月(1)DllMain和多线程死锁
关于本文
本文是我在查一个ATL singleton中的线程为何不能完全退出的bug时,查阅多本相关书籍,做了多个测试程序得出的结果。如有不正确的地方,欢迎指正。
正文
在Windows操作系统中,DLL(动态链接库)技术有很多优点。例如,多个应用程序可以共享一个DLL文件,真正实现了资源共享,大大缩小了应用程序的执行代码,有效地利用了内存,而且DLL文件作为一个单独的程序模块,封装性、独立性好,有利于提高软件开发和维护的效率。
DllMain入口指针,当进程和线程启动和终止时被系统调用,分别进行创建资源和释放资源等操作,特别地,也可以在DLL响应DLL_PROCESS_ATTACHDLL响应DLL_PROCESS_DETACHDllMain中无论是创建DllMain的顺序调用规则。
Windows操作系统中是顺序调用DLL的入口函数DllMain的。当进程被创建时,系统也为该进程创建了一个互斥对象。每个进程都有它自己的互斥对象。进程互斥对象的一个作用是,序列化在需要调用DllMain的种情况下DllMain的执行:DLL_PROCESS_ATTACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH和DLL_PROCESS_DETACHDLL。DllMain函数的第二个参数指示出调用DllMain的原因。
中创建DllMain的这个顺序调用规则,程序就会发生死锁。下面就DllMain中创建
考虑在一个多线程程序中,某个DLL被加载进程地址空间时,该DLL的DllMain启动了一个线程,然后立即调用一个应答事件对象的WaitForSingleObject函数,以确认在继续进行其余的DllMain处理之前,新产生的线程能够正确地执行一些操作。类似的代码如下:
HANDLEthread_handle=NULL该DLL内部线程的句柄
DWORDthread_id该DLL内部线程的
应答事件的句柄
DWORD WINAPI InSideDll_ThreadProc(LPVOID p
的操作完成后,
BOOL APIENTRY DllMain(HANDLE hModule DWORD ul_reason_for_call,LPVOID lpReserved switch(ul_reason_for_call
正在映射到进程地址空间中
DisableThreadLibraryCalls((
//创建DLL内线程使用的事件对象
g_hEvent=:CreateEvent(NULL,FALSE,FALSE,_T(hello11
//创建DLL内线程对象
g_thread_handle=:CreateThread(NULL InSideDll_ThreadProc,(LPVOIDthread_id
//等待刚创建的线程完成相关操作
:WaitForSingleObject(g_hEvent,INFINITE
//清除资源
:CloseHandle(g_ g_ g_thread_handle=NULL
:CloseHandle(g_hEvent g_hEvent=NULL
正在从进程地址空间中卸载
如果对这样的程序进行调试,通过Call Stack窗口可以看到该程序正在等待DllMain内部的线程处理,而Output窗口中也没有打印出--operations.--语句。可见线程函数InSideDll_ThreadProc根本就没有得到运行的机会。
结合DllMain的顺序调用规则,答案就很简单了。在程序运行过程中,第一个线程对LoadLibrary的调用引起操作系统获取进程互斥对象并以DLL_PROCESS_ATTACH值调用该DLL的DllMain。该DLL的DllMain函数产生第二个线程。无论何时当进程产生一个新线程时,操作系统将获取进程互斥对象,以便于它可以为DLL_THREAD_ATTACH值调用每个加载的DLL的DllMain函数。在这个特定的程序中,第二个线程阻塞,因为第一个线程还保持着进程互斥对象。不幸的是,第一个线程然后调用WaitForSingleObject确认第二个线程能够正确地完成一些操作。因为第二个线程被阻塞在进程互斥对象上,这个进程互斥对象还被第一个线程所持有,而第一个线程要等待第二个线程从而也被阻塞,结果就导致了死锁。如下图所示。
另外,DisableThreadLibraryCalls函数并不能解除这种死锁,相关原因在《Windows核心编程》一书中有更详尽的描述,这里就不再
显示全部