如果在堆上定义,QProcess 不会 kill/terminate 进程
QProcess doesn't kill/terminate the process if be defined on heap
我想kill/terminate我在应用程序退出时创建的进程:
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: Destroyed while process ("caffeinate") 仍然是 运行宁."
对于版本 2:我的应用程序可以启动进程但不能 kill/terminate 退出进程,并且没有像上面那样的报告。
我只想问一下为什么QProcess在堆上创建的时候不能像stative版本那样被kill掉?谢谢!
(我使用 struct Lambda 因为我不能在我的项目中使用 c++11 lambda)
tl;博士
在这两种情况下,信号都没有传递;在第一种情况下,析构函数会终止进程,在第二种情况下,它甚至没有机会 运行.
总的来说,您的代码是对几乎所有 QObject
、QThread
、信号等禁忌事项的一个很好的概括; 阅读 Threads and QObjects before doing anything with threads, QObjects and signals in Qt. This is essential information without which you'll only do a mess like this.* Also this wiki 文章提供了很好的 运行在 Qt 中使用线程的“正确方法”。
详细解释
让我们调用主线程线程A和QtConcurrent::run
启动的线程线程B.
案例一
当run
是来自第二个线程的运行时,p
被创建,所以它有thread affinity with thread B. For this reason, all the connect
you perform on it are queued connections(connect
的默认是AutoConnection
,如果连接的对象具有不同的线程关联,则使用 QueuedConnection
- 并且 qApp
在 线程 A).
中创建
问题是,排队的连接只有在接收线程有事件循环 运行ning 时才工作(它们被实现为 sendEvent
,所以如果目标中没有事件循环处理事件thread 他们只堆积在事件队列中),而这里 run
returns 刚开始进程。
因此,kill
、terminate
、close
和 deleteLater
永远不会被调用。请注意:
- 在这种情况下调用
deleteLater
无论如何都会出错,因为它会尝试对 static
对象执行 delete
;
kill
和 terminate
都不是同步的,因此要确保进程在继续之前已经停止,您还需要 waitForFinished
;
- 另外,
QtConcurrent::run
旋转的线程可能会在 run
终止后死掉1;这绝对是一件坏事,因为您将有 QObject
s 与死线程的线程亲和性一起放置。我不知道sendEvent
如何优雅地处理这种情况。
无论如何,当程序结束时,p
的析构函数将作为 C++ 应用程序关闭的正常部分自动调用2; as documented,QProcess
的析构函数终止它链接到的进程,如果它仍然是 运行ning(但也会写出你看到的“可怕的消息”)。
案例二
与案例 1 一样,您正在创建具有 线程 B 关联的 QProcess
;所以我们上面所说的所有关于未交付的事件 & co。仍然适用。
这里主要有以下三个区别:
- 您正在将
p
的父级设置为 qApp
,它位于主线程中;这是明确不允许的,QObject 之间的所有父子关系必须存在于具有相同线程关联的对象之间;可能您在控制台中收到了有关此事实的一些警告消息(setParent
明确检查对象是否存在于同一线程中,我希望 QObject
的构造函数执行相同的操作);
- 在这种情况下,
deleteLater
可能是合适的(如果你有一个事件循环旋转),因为你分配了 new
;
- 但最重要的是,这里
p
的析构函数从未被调用,因为它已分配给 new
并且没有人在其上调用 delete
;由于这个原因,启动的进程保持 运行ning(另外,你有一个小的内存泄漏)。
那么,处理这个问题的正确方法是什么?就个人而言,我会完全避免使用线程和信号。启动进程已经是异步的,因此您可以简单地完成:
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
使用全局线程池,它只会在空闲 30 秒后杀死旋转线程。
一般提示:您通常不希望以这种方式销毁“复杂”对象,因为 main
已经终止,所以 (1) 这会使调试变得更加复杂和 (2) 如果您有依赖 QApplication
的 Qt 对象仍然存在(通常是 QtGui 和 QtWidgets 中的所有内容),您将在程序终止时开始出现奇怪的崩溃。
我想kill/terminate我在应用程序退出时创建的进程:
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: Destroyed while process ("caffeinate") 仍然是 运行宁."
对于版本 2:我的应用程序可以启动进程但不能 kill/terminate 退出进程,并且没有像上面那样的报告。
我只想问一下为什么QProcess在堆上创建的时候不能像stative版本那样被kill掉?谢谢!
(我使用 struct Lambda 因为我不能在我的项目中使用 c++11 lambda)
tl;博士
在这两种情况下,信号都没有传递;在第一种情况下,析构函数会终止进程,在第二种情况下,它甚至没有机会 运行.
总的来说,您的代码是对几乎所有 QObject
、QThread
、信号等禁忌事项的一个很好的概括; 阅读 Threads and QObjects before doing anything with threads, QObjects and signals in Qt. This is essential information without which you'll only do a mess like this.* Also this wiki 文章提供了很好的 运行在 Qt 中使用线程的“正确方法”。
详细解释
让我们调用主线程线程A和QtConcurrent::run
启动的线程线程B.
案例一
当run
是来自第二个线程的运行时,p
被创建,所以它有thread affinity with thread B. For this reason, all the connect
you perform on it are queued connections(connect
的默认是AutoConnection
,如果连接的对象具有不同的线程关联,则使用 QueuedConnection
- 并且 qApp
在 线程 A).
问题是,排队的连接只有在接收线程有事件循环 运行ning 时才工作(它们被实现为 sendEvent
,所以如果目标中没有事件循环处理事件thread 他们只堆积在事件队列中),而这里 run
returns 刚开始进程。
因此,kill
、terminate
、close
和 deleteLater
永远不会被调用。请注意:
- 在这种情况下调用
deleteLater
无论如何都会出错,因为它会尝试对static
对象执行delete
; kill
和terminate
都不是同步的,因此要确保进程在继续之前已经停止,您还需要waitForFinished
;- 另外,
QtConcurrent::run
旋转的线程可能会在run
终止后死掉1;这绝对是一件坏事,因为您将有QObject
s 与死线程的线程亲和性一起放置。我不知道sendEvent
如何优雅地处理这种情况。
无论如何,当程序结束时,p
的析构函数将作为 C++ 应用程序关闭的正常部分自动调用2; as documented,QProcess
的析构函数终止它链接到的进程,如果它仍然是 运行ning(但也会写出你看到的“可怕的消息”)。
案例二
与案例 1 一样,您正在创建具有 线程 B 关联的 QProcess
;所以我们上面所说的所有关于未交付的事件 & co。仍然适用。
这里主要有以下三个区别:
- 您正在将
p
的父级设置为qApp
,它位于主线程中;这是明确不允许的,QObject 之间的所有父子关系必须存在于具有相同线程关联的对象之间;可能您在控制台中收到了有关此事实的一些警告消息(setParent
明确检查对象是否存在于同一线程中,我希望QObject
的构造函数执行相同的操作); - 在这种情况下,
deleteLater
可能是合适的(如果你有一个事件循环旋转),因为你分配了new
; - 但最重要的是,这里
p
的析构函数从未被调用,因为它已分配给new
并且没有人在其上调用delete
;由于这个原因,启动的进程保持 运行ning(另外,你有一个小的内存泄漏)。
那么,处理这个问题的正确方法是什么?就个人而言,我会完全避免使用线程和信号。启动进程已经是异步的,因此您可以简单地完成:
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
使用全局线程池,它只会在空闲 30 秒后杀死旋转线程。一般提示:您通常不希望以这种方式销毁“复杂”对象,因为
main
已经终止,所以 (1) 这会使调试变得更加复杂和 (2) 如果您有依赖QApplication
的 Qt 对象仍然存在(通常是 QtGui 和 QtWidgets 中的所有内容),您将在程序终止时开始出现奇怪的崩溃。