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

标签 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:进程(“含咖啡因”)仍在运行时被破坏。”

对于版本2:我的应用程序可以启动该过程,但无法在退出时终止/终止该过程,并且没有上述报告。

我只想问为什么在堆上创建QProcess时不能像静态版本那样将其杀死?谢谢!

(我使用struct Lambda是因为我无法在项目中使用c++ 11 lambda)

最佳答案

tl; dr
在两种情况下,信号都不会传递。在第一种情况下,析构函数杀死了进程,在第二种情况下,它甚至没有机会运行。
一般而言,您的代码是QObjectQThread,signal等几乎所有禁忌措施的一个很好的纲要; 在对Qt中的线程,QObject和信号进行任何操作之前,请先阅读Threads and QObjects这是必不可少的信息,没有这些信息,您只会像这样乱七八糟。*而且this维基文章也很好地总结了在Qt中使用线程的“正确方法”。

详细说明
我们将主线程称为A,将其称为QtConcurrent::run线程B。
情况1
当从第二个线程运行run时,创建了p,因此它带有带有线程B的thread affinity。因此,您在其上执行的所有connect都是queued connections(connect的默认值为AutoConnection,如果连接的对象使用QueuedConnection具有不同的线程相似性-并且在线程A)中创建了qApp
问题是,只有在接收线程运行了事件循环的情况下,排队连接才起作用(它们实现为 sendEvent ,因此,如果目标线程中没有事件循环处理事件,它们只会堆积在事件队列中),而在这里run在开始该过程后立即返回。
因此,永远不会调用killterminateclosedeleteLater。注意:

无论如何,

  • 在这种情况下调用deleteLater都会是一个错误,因为它将试图在delete对象上执行static
  • killterminate都不是同步的,因此要确保该过程已终止,您还需要一个waitForFinished
  • 也可能是,QtConcurrent::run终止后,由run旋转的线程可能会死掉;这绝对是一件坏事,因为您将在QObject周围放置与已死线程的线程亲和力。我不知道sendEvent如何处理这种情况。

  • 无论如何,当程序结束时,p的析构函数会自 Action 为C++应用程序关闭的正常部分2调用; QProcess的析构函数as documented在其仍在运行的情况下终止与其链接的进程(但还会写出您看到的“可怕消息”)。
    情况二
    与情况1一样,您正在创建具有线程B相似性的QProcess;因此,我们上面所说的有关 Activity 未交付和合作的所有信息。仍然适用。
    这里有三个主要区别:
  • 您将p的父级设置为qApp,它位于主线程中;明确禁止这样做,QObject之间的所有父子关系必须存在于具有相同线程亲和力的对象之间;可能您正在控制台中收到有关此事实的警告消息(setParent明确检查对象是否位于同一线程中,我希望QObject的构造函数执行相同的操作);
  • 在这种情况下,使用
  • 分配
  • ;如果是
  • ,则deleteLater可能是合适的(如果您有一个事件循环旋转);
  • ,但最重要的是,这里从未调用new的析构函数,因为它已经分配了p,并且没有人在上面调用new。因此,启动的进程将继续运行(而且,您的内存泄漏也很小)。

  • 那么,解决这个问题的正确方法是什么?就我个人而言,我会完全避免使用线程和信号。启动进程已经是异步的,因此您只需完成以下操作即可:
    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;
    }
    
    与线程,事件队列,信号等一如既往:不要使其变得比需要的复杂。

    脚注
  • 实际上,在这种情况下,您可能不会注意到,因为delete使用全局线程池,该池仅在闲置30秒后才杀死旋转线程。
  • 一般提示:您通常不希望以这种方式销毁“复杂”的对象,因为QtConcurrent已经终止,因此(1)这会使调试更加复杂,(2)如果您有依赖于Qt对象的Qt对象, main仍然有效(通常是QtGui和QtWidgets中的所有内容),您将在程序终止时开始出现奇怪的崩溃。
  • 关于c++ - 如果在堆上定义,QProcess不会终止/终止进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32645706/

    相关文章:

    c++ - 即使使用 "new"关键字,二进制搜索树插入也不会在堆上创建数据

    C++ Windows JPEG 到字节流

    C++11 模板,确定返回类型

    c++ - 为什么 C++11 将析构函数标记为 nothrow,是否可以覆盖它?

    创建一个静态库并链接到它

    java : Get heap dump without jmap or without hanging the application

    c++ - WinAPI C++ 如何从 64 位应用程序加载资源

    java - Java中的初始化顺序: exception with Netty 4. 0.7

    swift - 我可以直接从协议(protocol)扩展类型访问默认静态变量吗?

    java - 导致 java.lang.OutOfMemoryError : PermGen space error 的算法