我正在为*nix操作系统编写多线程C++应用程序。如何优雅地终止这样一个应用程序?我的想法是安装一个SIGINT(SIGTERM?)信号处理程序,停止/联结我的线程。同时,是否可能“保证”在处理信号时调用所有析构函数(前提是没有其他错误或异常抛出)?
我正在为*nix操作系统编写多线程C++应用程序。如何优雅地终止这样一个应用程序?我的想法是安装一个SIGINT(SIGTERM?)信号处理程序,停止/联结我的线程。同时,是否可能“保证”在处理信号时调用所有析构函数(前提是没有其他错误或异常抛出)?
有几点需要考虑:
指定一个线程来负责协调关闭,例如,正如Dithermaster所建议的那样,如果您正在编写独立应用程序,则可以将其设置为主线程。或者,如果您正在编写库,则提供接口(例如函数调用),使客户端程序可以终止在库中创建的对象。
您无法保证会调用析构函数;这取决于您,需要仔细地为每个new调用删除。也许智能指针可以帮助您。但是,这确实是一个设计问题。主要组件应具有启动和停止语义,您可以选择从类构造函数和析构函数中调用它们。
一组交互对象的关闭序列是可能需要一些努力才能正确获取的。例如,在删除对象之前,您确定没有计时器机制会在几微/毫/秒后尝试调用它吗?在此,尝试和错误是您的朋友;开发一个可以重复快速启动和停止应用程序以解决关闭相关竞争条件的框架。
信号是触发事件的一种方式;其他方法可能是周期性轮询已知文件,或打开套接字并在其上接收某些数据。无论哪种方式,您都希望将关闭序列代码与触发事件分离。
我的建议是主线程在退出之前关闭所有工作线程。向每个工作线程发送一个事件,告诉它清理并退出,并等待每个工作线程执行完毕。这将允许所有C++析构函数运行。
sig_atomic_t
类型的变量(可能带有 volatile
限定符)并返回。通常情况下你不能调用大多数函数,也必须不写入全局内存。换句话说,处理程序应该只设置一个标志,在适当的时候在主程序中进行测试,并从那里执行由信号本身导致的操作。longjmp()
或甚至是 exit()
)不会触发析构函数。
关于通用的关闭程序实践,在该领域存在不同的观点。
有些人认为应该执行“优雅终止”,即释放所有已分配的资源。在C++中,这通常意味着在进程终止之前必须正确执行所有析构函数。在实践中,这很棘手,而且常常是多线程程序的主要问题之一,原因有很多。信号通过异步信号分发的本质进一步复杂化了事情。
因为大部分工作都是完全无用的,像我这样的其他人认为程序必须立即终止,可能在撤消对系统的持久更改(如删除临时文件或恢复屏幕分辨率)并保存配置后不久。一个表面上更整洁的清理不仅是浪费时间的(因为操作系统将清理大部分像分配的内存、悬空线程和打开的文件描述符之类的东西),而且可能是一个严重的时间浪费(解除分配器可能会触及页出内存,无用地迫使系统将它们分页以便在进程终止后不久释放它们,例如),更不用提由加入线程引起死锁的可能性。
只需说不。当你想离开时,调用exit()
(甚至是_exit()
,但要注意未刷新的I/O),就这样。比启动慢的程序更烦人的是终止缓慢的程序。
signal handler
中也允许使用 longjmp
,可能还包括 throw
。 - Ben Voigtpthread_kill()
和longjmp()
,显而易见的原因。最后,我不了解在多线程POSIX应用程序中,信号处理程序内部使用throw
时的语义。 - alecov