我想杀死/终止我的应用退出时创建的进程:
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)
答案 0 :(得分:3)
在这两种情况下,都没有传递信号;在第一种情况下,析构函数会终止进程,在第二种情况下,它甚至没有机会运行。
一般来说,您的代码是几乎所有禁止使用QObject
,QThread
,信号等的简单汇编; 在使用Qt中的线程,QObject和信号执行任何操作之前,请先阅读Threads and QObjects。 这是必不可少的信息,没有这些信息,您只会做这样的混乱。*此外,this wiki文章提供了使用Qt线程的“正确方法”的良好概述。
让我们调用主线程线程A ,线程由QtConcurrent::run
线程B 启动。
从第二个线程运行run
时,会创建p
,因此它具有thread affinity并且线程 B 。因此,您在其上执行的所有connect
都是queued connections(connect
的默认值为AutoConnection
,如果连接的对象具有QueuedConnection
,则使用qApp
不同的线程关联 - 在线程A 中创建run
。
问题是,排队连接仅在接收线程运行事件循环时才起作用(它们实现为sendEvent
,因此如果目标线程中没有事件循环处理事件,它们只会堆积在事件队列),此处kill
在启动过程后立即返回。
因此,永远不会调用terminate
,close
,deleteLater
和deleteLater
。请注意:
delete
无论如何都会出错,因为它会尝试在static
对象上执行kill
; terminate
和waitForFinished
都不是同步的,所以为了确保流程在继续之前已经死亡,您还需要QtConcurrent::run
; run
终止 1 之后,由QObject
旋转的线程可能会死亡;这绝对是一件坏事,因为你将sendEvent
与线程关联到一个死的线程。我不知道p
处理这种情况有多优雅。无论如何,当程序结束时,QProcess
的析构函数会自动调用,作为关闭C ++应用程序 2 的正常部分; as documented,QProcess
的析构函数会终止它链接到的进程(如果它仍然在运行)(但也会写出你看到的“可怕消息”)。
与案例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
与线程,事件队列,信号等一样:不要让它变得比它需要的更复杂。
在这种情况下实际上你可能不会注意到因为main
使用了全局线程池,它只在空闲30秒后杀死了线程。
一般提示:您通常不希望以这种方式销毁“复杂”对象,因为QApplication
已经终止,因此(1)这会使调试变得更加复杂,并且(2)如果你有依赖于{{1}}仍然存在的Qt对象(通常是QtGui和QtWidgets中的所有内容),你将在程序终止时开始出现奇怪的崩溃。