线程对象的WaitForSingleObject在DLL卸载中不起作用 [英] WaitForSingleObject for thread object does not work in DLL unload

查看:399
本文介绍了线程对象的WaitForSingleObject在DLL卸载中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当DLL被卸载时,我偶然发现了Windows线程机制的意外行为。 A有一包工作线程对象,当DLL被卸载(通过DllMain DLL_PROCESS_DETACH)时,我试图完美地完成它们。代码很简单(我发送一个事件来完成线程的等待循环):

I've stumbled upon an unexpected behavior of Windows thread mechanism when DLL is unloaded. A have a pack of worker thread objects and I'm trying to finish them graciously when DLL is unloaded (via DllMain DLL_PROCESS_DETACH). The code is very simple (I do send an event to finish the thread's wait loop):

WaitForSingleObject( ThrHandle, INFINITE );
CloseHandle( ThrHandle );

然而,WaitForSingleObject挂起了整个事情。如果在DLL被卸载之前执行,它的工作正常。如何解决这个问题?

Yet the WaitForSingleObject hangs the whole thing. It works fine if I perform it before DLL is unloaded. How this behavior can be fixed?

推荐答案

你不能等待线程退出DllMain()。除非线程已经在DLL_PROCESS_DETACH被接收的时候退出,所以将永远死锁。这是预期的行为。

You can't wait for a thread to exit in DllMain(). Unless the thread had already exited by the time the DLL_PROCESS_DETACH was received, doing so will always deadlock. This is the expected behaviour.

原因是通过加载程序锁定对DllMain()的调用进行了序列化。当ExitThread()被调用时,它会声明加载器锁,以便它可以使用DLL_THREAD_DETACH调用DllMain()。直到该通话完成,线程仍在运行。

The reason for this is that calls to DllMain() are serialized, via the loader lock. When ExitThread() is called, it claims the loader lock so that it can call DllMain() with DLL_THREAD_DETACH. Until that call has finished, the thread is still running.

所以DllMain正在等待线程退出,线程正在等待DllMain退出,一个经典的僵局

So DllMain is waiting for the thread to exit, and the thread is waiting for DllMain to exit, a classic deadlock situation.

另请参见动态链接库最佳实践在MSDN上。

See also Dynamic-Link Library Best Practices on MSDN.

解决方案是为应用程序添加一个新函数到您的DLL中在卸载DLL之前调用。正如你所说,您的代码已经很好地显示出来了。

The solution is to add a new function to your DLL for the application to call before unloading the DLL. As you have noted, your code already works perfectly well when called explicitly.

在向后兼容性要求使得不可能添加这样的功能的情况下,如果您必须具有工作线程,请考虑将DLL分为两部分,其中一个由另一部分动态加载。动态加载的部分将至少包含工作线程所需的所有代码。

In the case where backwards compatibility requirements make adding such a function impossible, and if you must have the worker threads, consider splitting your DLL into two parts, one of which is dynamically loaded by the other. The dynamically loaded part would contain (at a minimum) all of the code needed by the worker threads.

当应用程序本身加载的DLL接收到DLL_PROCESS_DETACH时,您只需设置事件来通知线程退出,然后立即返回。其中一个线程必须被指定等待所有其他人,然后释放第二个DLL,您可以使用 FreeLibraryAndExitThread()

When the DLL that was loaded by the application itself receives DLL_PROCESS_DETACH, you just set the event to signal the threads to exit and then return immediately. One of the threads would have to be designated to wait for all the others and then free the second DLL, which you can do safely using FreeLibraryAndExitThread().

(视情况而定,特别是如果工作线程正在退出和/或新建作为常规操作的一部分,您可能需要非常小心避免竞争条件和/或死锁;如果您使用线程池和回调函数,这可能会更简单而不是手动创建工作线程。)

(Depending on the circumstances, and in particular if worker threads are exiting and/or new ones being created as part of regular operations, you may need to be very careful to avoid race conditions and/or deadlocks; this would likely be simpler if you used a thread pool and callbacks rather than creating worker threads manually.)

在特殊情况中,线程不需要要使用任何非常简单的Windows API,可能使用线程池和工作回调来避免需要第二个DLL。一旦回调已经退出,您可以使用 WaitForThreadpoolWorkCallbacks(),图书馆的安全可以卸载 - 您不需要等待线程自己退出。

In the special case where the threads do not need to use any but the very simplest Windows APIs, it might be possible to use a thread pool and work callbacks to avoid the need for a second DLL. Once the callbacks have exited, which you can check using WaitForThreadpoolWorkCallbacks(), it is safe for the library to be unloaded - you do not need to wait for the threads themselves to exit.

抓住这里是回调必须避免任何可能采取加载程序锁定的Windows API。没有记录哪些API调用在这方面是安全的,并且在不同版本的Windows之间有所不同。如果你打电话比SetEvent或WriteFile更复杂,比如说,或者如果你使用的是一个库而不是本地的Windows API函数,那么你不能使用这个方法。

The catch here is that the callbacks must avoid any Windows APIs that might take the loader lock. It is not documented which API calls are safe in this respect, and it varies between different versions of Windows. If you are calling anything more complicated than SetEvent or WriteFile, say, or if you are using a library rather than native Windows API functions, you must not use this approach.

这篇关于线程对象的WaitForSingleObject在DLL卸载中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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