当一个进程结束时,处于Sleep()中的线程会发生什么?

3

我正在使用一个启动和结束线程的类。该线程在构造函数中创建。线程函数有一个循环,只要标志为TRUE就会继续运行。这个标志是类的静态成员。析构函数将标志设置为FALSE。这样,每个类实例都有一个关联的线程,该线程在实例的生命周期内运行。

我试图理解析构函数何时运行以及这是否是结束线程的正确方法。我没有太多的多线程经验。

下面是我的理解。在析构函数中,标志将被设置为FALSE。假设Sleep()无限运行。对象被销毁,但标志仍然存在于内存中,因为它是静态的。但整个进程正在结束,所以在某个时刻静态标志将消失。标志会在线程之前消失吗?如果线程被强制返回并且进程正在结束,线程是否仍然关心标志?我不知道发生了什么。

我正在使用Visual Studio 2010中的Visual C++。


1
在析构函数中更改标志后,您可以等待线程完成。 - Dipto
3个回答

2
在Windows上,线程的生命周期小于或等于创建它的进程。因此,一旦进程结束,线程也会结束。
正常的进程关闭不会完成,直到该进程中的所有线程终止。因此,在这种情况下,标志将被设置,主线程可能终止,但创建的后台线程将继续运行。最终,它们将看到标志的FALSE值,退出循环,完成并使进程关闭完整。

最终,当等待Windows在卸载DLL时已经停止的线程时,你会遇到死锁。请参阅MSDN中的Loadlibrary()/FreeLibrary()。我曾经历过这种情况。最佳实践是:(1)永远不要无限期地等待任何模块卸载,并且(2)始终在卸载模块之前调用/使用清理例程,该例程创建其自己的线程。这将节省一些麻烦。 - JensG

2
请注意,当某个对象正在被其他线程使用(而不是静态标志)时运行析构函数会产生未定义的结果。
想象一下,如果线程在处理过程中而不是检查存活标志,会发生什么,机会很小吧。
最好编写一个stop(bool wait)函数,这样如果调用析构函数并需要自动清理,则将标志设置为停止,并阻塞直到线程设置另一个标志为“已停止”,或者只需使用pthread_join加入该线程(不建议,请参见下文)。
此外,当您阻塞以进行优雅的终止时,还可以设置超时时间,如果出现问题,则强制终止线程(并生成调试警报)。

在关闭过程中也适用于通常的口号“不要在GUI事件处理程序中进行阻塞调用”。 - Martin James
是的,GUI线程不应该被阻塞,你应该停止线程,并在析构函数中以异步方式等待它实际停止(例如使用消息)。在析构函数中停止线程应该只作为安全的最后手段。 - Non-maskable Interrupt

0

这取决于“析构函数”是否真正运行。

如果您尝试在某个“OnClose”事件处理程序中“析构”所描述的对象,则设置静态标志将指示线程在检查它时终止。如果他们没有,线程将继续休眠(或停留在任何阻塞调用上,或继续运行代码)。

如果您不采取进一步的操作等待对象线程终止,则主GUI线程将继续运行,销毁其所有GUI对象等,并调用ExitProcess()。

一旦调用ExitProcess(),操作系统将在释放任何内存(例如包含您的标志的内存)之前停止进程的所有线程,无论它们处于什么状态。

调用ExitProcess()的线程永远不会返回控制权。属于同一进程且未在另一个核心上运行的其他线程将其状态设置为永远不再运行。属于同一进程且正在另一个核心上运行的其他线程将被硬件中断以停止线程。

标志会在线程之前消失吗?

不会。在托管标志的内存被释放之前,线程将被停止。

如果您不希望发生强制终止,您必须采取行动等待对象线程的实际终止。您必须通过在OnClose处理程序中设置适当的CloseAction来延迟主GUI线程调用ExitProcess。当所有对象线程已终止并且对象的析构函数已完成时,GUI线程应仅关闭/释放自身。

如果我真的、真的、真的必须这样做,最好通过向主GUI线程PostMessaging一个“WM_THREADGONE”Windows消息(作为对象线程在实际终止之前的最后一个操作),并在消息处理程序中向下计数“threadCount”,以便保持GUI线程可用以处理消息,直到所有对象线程自行终止。像Join()这样的硬等待机制只会产生死锁,并具有巨大的关机问题容量,不应使用。

通常,我尝试通过设计我的应用程序,使突然的、非自愿的线程终止成为可接受的操作,然后让Exitprocess()将一切都清除掉,从而解决所有线程终止问题。虽然这并非总是可能的,但如果您能够做到这一点,那么它会更加安全。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接