如果在堆上定义,QProcess 不会终止/终止进程



我想在我的应用程序退出时杀死/终止我创建的进程:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QPushButton w; w.show();
    struct Lambda {
        static void run() {
            static QProcess p; //version 1
//            QProcess& p = *new QProcess(qApp); //version 2
            p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::kill);
            p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::terminate);
            p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::close);
            p.connect(qApp, &QApplication::aboutToQuit, &p, &QProcess::deleteLater);
            p.start("caffeinate -d");
        }
    };
    QtConcurrent::run(Lambda::run);
    return a.exec();
}

版本1:我的应用程序运行如我所期望的:创建并杀死进程成功,但当退出应用程序时,QCreator报告:"QProcess:销毁而进程("caffeinate")仍在运行。"

对于版本2:我的应用程序可以启动进程,但不能在退出时杀死/终止进程,并且没有上面的报告。

我只是想问为什么在堆上创建时,QProcess不能像静态版本一样被杀死?谢谢!

(我使用struct Lambda,因为我不能在我的项目中使用c++11 Lambda)

br

在这两种情况下,信号都没有传递;在第一种情况下,析构函数杀死进程,在第二种情况下,它甚至没有机会运行。

一般来说,你的代码是一个很好的汇编,几乎所有的禁止使用QObject, QThread,信号等;在Qt中处理线程、qobject和信号之前先读取线程和qobject .这是必不可少的信息,如果没有它,你只会做这样的混乱。*这篇维基文章也提供了一个很好的"正确方法"的概述;在Qt中使用线程


详细说明

我们称主线程为线程A,称QtConcurrent::run 启动的线程B

案例1

run从第二个线程运行时,创建了p,因此它与线程B具有线程亲和性。由于这个原因,您在它上面执行的所有connect都是排队连接 (connect的默认值是AutoConnection,如果连接的对象具有不同的线程亲和性,则使用QueuedConnection -并且qApp是在线程a 中创建的)。

问题是,排队连接只有在接收线程有一个事件循环运行时才能工作(它们被实现为sendEvent,所以如果目标线程中没有事件循环处理事件,它们只会堆积在事件队列中),而这里run在启动进程后立即返回。

因此,kill, terminate, closedeleteLater永远不会被调用。注意:

  • 在这种情况下调用deleteLater将是一个错误,因为它将试图在static对象上执行delete;
  • killterminate都不是同步的,所以为了确保进程在继续之前已经死亡,你还需要一个waitForFinished;
  • 同样,QtConcurrent::run旋转的线程可能会在run终止1后死亡;这绝对是一件坏事,因为您将让QObject s与已死的线程具有线程亲和关系。我不知道sendEvent如何优雅地处理这种情况。

无论如何,当程序结束时,p的析构函数作为c++应用程序关闭的正常部分被自动调用2;如文档所述,QProcess的析构函数终止与其链接的进程,如果该进程仍在运行(但也会写出"可怕的消息")。你看到).

案例2

与情况1一样,您正在创建具有线程B亲和力的QProcess;因此,我们上面所说的关于事件未交付的所有内容& &;

这里有三个主要的区别:

  • 你正在将p的父进程设置为qApp,它位于主线程中;这是明确不允许的,qobject之间的所有父子关系必须存在于具有相同线程亲和性的对象之间;可能你在控制台得到一些关于这个事实的警告消息(setParent显式检查对象是否在同一线程中,我期望QObject的构造函数也这样做);
  • 在这种情况下,deleteLater可能是合适的(如果您有一个事件循环旋转),因为您分配了new;
  • 但最重要的是,这里p的析构函数从来没有被调用过,因为它已经被分配了new,没有人在它上面调用delete;由于这个原因,启动的进程继续运行(同时,您有一个小的内存泄漏)。

那么,处理这个问题的正确方法是什么呢?就我个人而言,我会完全避免线程和信号。启动进程已经是异步的,因此您可以简单地执行以下操作:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QProcess p;
    p.start("caffeinate -d");
    QPushButton w; w.show();
    int ret = a.exec();
    p.close();
    return ret;
}
与线程、事件队列、信号等一样,不要把它弄得太复杂。

脚注

  1. 在实际情况中,您可能不会注意到,因为QtConcurrent使用全局线程池,它只在空闲30秒后杀死旋转的线程。

  2. 一般提示:你通常不想要"复杂的";对象将以这种方式销毁,因为main已经终止,所以(1)这使得调试更加复杂,(2)如果您有依赖于QApplication仍然存活的Qt对象(通常,QtGui和QtWidgets中的所有内容),您将开始在程序终止时获得奇怪的崩溃。

最新更新