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

时间:2015-09-18 06:56:19

标签: c++ static heap-memory kill-process 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不能像stative版本一样杀死?谢谢!

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

1 个答案:

答案 0 :(得分:3)

TL;博士

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

一般来说,您的代码是几乎所有禁止使用QObjectQThread,信号等的简单汇编; 在使用Qt中的线程,QObject和信号执行任何操作之前,请先阅读Threads and QObjects 这是必不可少的信息,没有这些信息,您只会做这样的混乱。*此外,this wiki文章提供了使用Qt线程的“正确方法”的良好概述。

详细说明

让我们调用主线程线程A ,线程由QtConcurrent::run 线程B 启动。

案例1

从第二个线程运行run时,会创建p,因此它具有thread affinity并且线程 B 。因此,您在其上执行的所有connect都是queued connectionsconnect的默认值为AutoConnection,如果连接的对象具有QueuedConnection,则使用qApp不同的线程关联 - 在线程A 中创建run

问题是,排队连接仅在接收线程运行事件循环时才起作用(它们实现为sendEvent,因此如果目标线程中没有事件循环处理事件,它们只会堆积在事件队列),此处kill在启动过程后立即返回。

因此,永远不会调用terminateclosedeleteLaterdeleteLater。请注意:

  • 在这种情况下调用delete无论如何都会出错,因为它会尝试在static对象上执行kill;
  • terminatewaitForFinished都不是同步的,所以为了确保流程在继续之前已经死亡,您还需要QtConcurrent::run;
  • 也可能run终止 1 之后,由QObject旋转的线程可能会死亡;这绝对是一件坏事,因为你将sendEvent与线程关联到一个死的线程。我不知道p处理这种情况有多优雅。

无论如何,当程序结束时,QProcess的析构函数会自动调用,作为关闭C ++应用程序 2 的正常部分; as documentedQProcess的析构函数会终止它链接到的进程(如果它仍然在运行)(但也会写出你看到的“可怕消息”)。

案例2

与案例1一样,您正在使用主题B 亲和关系创建p;所以我们上面所说的关于没有交付的事件和合。仍然适用。

这里有三个主要区别:

  • 您将qApp的父级设置为setParent,它位于主线程中;这是明确禁止的,QObject之间的所有父子关系必须存在于具有相同线程关联的对象之间;可能你在控制台中收到一些关于这个事实的警告消息(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; } ;因此,启动的进程会继续运行(同样,内存泄漏也很少)。

那么,处理这个问题的正确方法是什么呢?就个人而言,我会完全避免线程和信号。启动一个进程已经是异步的,所以你可以简单地完成:

QtConcurrent

与线程,事件队列,信号等一样:不要让它变得比它需要的更复杂。

脚注

  1. 在这种情况下实际上你可能不会注意到因为main使用了全局线程池,它只在空闲30秒后杀死了线程。

  2. 一般提示:您通常不希望以这种方式销毁“复杂”对象,因为QApplication已经终止,因此(1)这会使调试变得更加复杂,并且(2)如果你有依赖于{{1}}仍然存在的Qt对象(通常是QtGui和QtWidgets中的所有内容),你将在程序终止时开始出现奇怪的崩溃。