0

我调试用 C++/Qt 5.12.1 编写的控制台多线程应用程序。它在 Linux Mint 18.3 x64 上运行。

这个应用程序有SIGINT处理程序QWebSocketServerQWebSocket表格。它使用close() QWebSocketServer并调用abort()/ deleteLater()forQWebSocket表中的项目来处理终止。

如果 websocket 客户端连接到这个控制台应用程序,那么终止会因为一些正在运行的线程而失败(我想它是内部QWebSocket线程)。如果没有连接,则终止成功。

如何解决?让应用程序优雅地退出。

4

2 回答 2

1

要优雅地退出套接字服务器,我们可以尝试:

最重要的部分是允许主线程事件循环运行并等待QWebSocketServer::closed()以便插槽调用QCoreApplication::quit()

即使使用以下方法也可以做到:

connect(webSocketServer, &QWebSocketServer::closed,
        QCoreApplication::instance(), &QCoreApplication::quit);

如果我们不需要更详细的反应。

在所有连接该信号之后,继续进行pauseAccepting()以防止更多连接。打电话QWebSocketServer::close

如果以上足够,则可能不需要以下内容。您需要先尝试上述方法,并且只有在仍有问题时才处理现有和待处理的连接。根据我的经验,行为在平台上有所不同,并且在服务器环境中具有一些独特的 websocket 实现(这可能只是适合您的 Qt)。

只要我们有一些带有QWebSocket实例的数组,我们就可以尝试调用QWebSocket::abort()所有这些实例来立即释放。这一步似乎是由问题作者描述的。

尝试使用QWebSocketServer::nextPendingConnection()迭代挂起的连接并调用它们。呼叫,如果这也有效。abort()deleteLater

于 2019-07-26T15:13:02.677 回答
1

没有必要做任何事情。“优雅退出”是什么意思?一旦有终止您的应用程序的请求,您应该立即使用exit(0)或类似的机制终止它。这就是“优雅退出”应该的样子。

笔记:我被改造了。我曾经认为优雅的退出是一件好事。它们通常是对 CPU 资源的浪费,通常表明应用程序的体系结构存在问题。

一个很好的理由说明为什么它应该这样写在kj框架中(capnproto 的一部分)。

引用肯顿瓦尔达的话:

KJ_NORETURN(virtual void exit()) = 0;

表示程序完成。该程序被认为是成功的,除非error()被调用。通常它以 退出_Exit(),这意味着堆栈没有展开,缓冲区没有刷新,等等 - 调用者有责任刷新任何重要的缓冲区。但是,例如用于单元测试目的的替代上下文实现可以选择抛出异常。

起初,这种方法可能听起来很疯狂。干净地关闭不是更好吗?如果丢失数据怎么办?然而,事实证明,如果您查看每个常见的程序类,_Exit() 几乎总是更可取。让我们分解一下:

  • 命令:您可能从命令行运行的典型程序是单线程的,并且可以快速且确定地退出。命令通常使用缓冲 I/O,并且需要在退出之前刷新这些缓冲区。但是,析构函数执行的大部分工作不是刷新缓冲区,而是释放内存、将对象放入空闲列表以及关闭文件描述符。如果进程无论如何都将退出,那么所有这些都无关紧要,并且对于快速运行的命令,释放堆空间所浪费的时间可能会对脚本的整体运行时间产生真正的影响。同时,通常很容易确定在退出之前需要刷新哪些资源,并且很容易判断它们是否没有被刷新(因为该命令无法产生预期的输出)。所以,命令明确地确保在退出之前刷新所有输出是相当容易的,并且无论如何他们这样做可能是一个好主意,因为应该检测和处理写入失败。对于命令,一个好的策略是在堆栈上分配任何需要干净销毁的对象,并允许它们在命令退出之前超出范围。同时,任何不需要清理的资源都应分配为命令主类的成员,其析构函数通常不会被调用。并允许它们在命令退出之前超出范围。同时,任何不需要清理的资源都应分配为命令主类的成员,其析构函数通常不会被调用。并允许它们在命令退出之前超出范围。同时,任何不需要清理的资源都应分配为命令主类的成员,其析构函数通常不会被调用。

  • 交互式应用程序:与用户交互的程序(无论是带有窗口的图形应用程序还是基于控制台的应用程序,如 emacs)通常仅在用户要求时退出。此类应用程序可能会将需要同步到磁盘的大型数据结构存储在内存中,例如文档或用户偏好。但是,依靠堆栈展开或全局析构函数作为确保此类同步发生的机制可能是错误的。首先,现在是 2013 年,应用程序应该在做出这些更改的那一刻主动将更改同步到非易失性存储。应用程序可能随时崩溃,并且崩溃永远不会丢失超过半秒的数据。同时,如果用户在存在未保存的更改时确实尝试关闭应用程序,应用程序 UI 应该提示用户决定要做什么。这样的 UI 机制显然太高级了,无法通过析构函数来实现,所以 KJ 的使用_Exit()在这里不应该有所作为。

  • 服务器:一个好的服务器是容错的,为随时可能崩溃的可能性做好准备,操作系统可能会决定将其关闭,或者运行它的机器可能会死机。所以,使用 _Exit() 应该没问题。事实上,服务器通常甚至从不调用 exit 。他们在外部被杀死。

  • 批处理作业:长时间运行的批处理作业介于命令和服务器之间。它可能确切地知道在退出之前需要刷新什么,并且它可能应该是容错的。

于 2019-07-26T16:30:28.437 回答