9

我正在为 *nix 操作系统编写一个多线程 c++ 应用程序。优雅终止此类应用程序的最佳实践是什么?我的直觉是我想在 SIGINT(SIGTERM?)上安装一个信号处理程序,它会停止/加入我的线程。此外,是否可以“保证”调用所有析构函数(前提是在处理信号时没有抛出其他错误或异常)?

4

3 回答 3

3

想到了一些考虑:

  • 指定 1 个线程负责协调关闭,例如,正如 Dithermaster 建议的那样,如果您正在编写独立应用程序,这可能是主线程。或者,如果您正在编写库,请提供一个接口(例如函数调用),客户端程序可以借此终止在库中创建的对象。

  • 你不能保证调用析构函数;这取决于您,并且需要为每个新的仔细调用删除。也许智能指针会帮助你。但是,实际上,这是一个设计考虑。主要组件应该具有启动和停止语义,您可以选择从类构造函数和析构函数中调用。

  • 一组交互对象的关闭顺序可能需要一些努力才能正确。例如,在您删除一个对象之前,您确定某些计时器机制不会在几微/毫秒/秒后尝试调用它吗?反复试验是您的朋友;开发一个可以重复快速启动和停止您的应用程序的框架,以梳理与关闭相关的竞争条件。

  • 信号是触发事件的一种方式;其他人可能会定期轮询已知文件,或打开套接字并接收一些数据。无论哪种方式,您都希望将关闭序列代码与触发事件分离。

于 2013-12-28T23:30:23.127 回答
2

我的建议是主线程在退出之前关闭所有工作线程。向每个工作人员发送一个事件,告诉它清理并退出,并等待每个工作人员这样做。这将允许所有 C++ 析构函数运行。

于 2013-12-28T23:11:12.257 回答
1

关于信号管理,您可以在信号处理程序中可移植安全地做的唯一一件事就是写入类型sig_atomic_t(可能是volatile限定的)变量并返回。一般来说,您不能调用大多数函数,也不能写入全局内存。换句话说,处理程序应该只在你的主程序中设置一个要测试的标志,在你认为合适的某个时候,应该从那里执行由信号本身产生的动作。

(由于可能涉及阻塞 I/O,请考虑研究POSIX Thread Cancellation。您的 Unix 克隆(尤其是 Linux)可能在这方面和上述方面具有特殊性。)

关于析构函数,不涉及任何魔法。如果控制通过语言中定义的任何方式离开给定范围,它们将被执行。通过其他方式(例如,longjmp()甚至exit())离开作用域不会触发析构函数。

对于一般的停工做法,业内众说纷纭。

一些人指出,应该执行“优雅终止”,即释放所有已分配的资源。在 C++ 中,这通常意味着所有析构函数都应该在进程终止之前正确执行。这在实践中很棘手,而且由于各种原因,特别是在多线程程序中,这通常会导致很多麻烦。由于异步信号调度的本质,信号使事情进一步复杂化。

因为大部分工作完全没用,像我这样的其他一些人认为程序必须立即终止,可能在撤消对系统的持久更改(如删除临时文件或恢复屏幕分辨率)并保存配置后不久。一个明显更整洁的清理不仅是浪费时间(因为操作系统会清理大多数东西,比如分配的内存、悬空线程和打开的文件描述符),而且可能会严重浪费时间(释放器可能会触及分页内存,毫无用处例如,强制系统分页它们只是为了在进程终止后立即释放它们),更不用说死锁可能源于加入线程。

拒绝吧。当你想离开时,打电话exit()(甚至_exit(),但要注意未刷新的 I/O),就是这样。比慢启动程序更烦人的是慢终止程序。

于 2013-12-29T01:24:09.883 回答