我想终止/终止我退出应用程序时创建的过程:
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
在两种情况下,信号都不会传递。在第一种情况下,析构函数杀死了进程,在第二种情况下,它甚至没有机会运行。
一般而言,您的代码是QObject
,QThread
,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
在开始该过程后立即返回。
因此,永远不会调用kill
,terminate
,close
和deleteLater
。注意:
无论如何,
deleteLater
都会是一个错误,因为它将试图在delete
对象上执行static
。 kill
和terminate
都不是同步的,因此要确保该过程已终止,您还需要一个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/