这个问题在这里已有答案:
如何终止在QThread中运行并被另一个QThread删除的正在进行的QProcess?我甚至插入了一个QMutex extCmdProcessLock,它应该在extCmdProcess完成或超时之前避免破坏DbManager . 如果另一个线程在DbManager上调用delete,我会在“waitForStarted”上遇到分段错误 . 我不能使用信号(我认为)因为我在顺序数据处理中使用外部命令 . 非常感谢您的帮助!
DbManager::extCmd(){
...
QMutexLocker locker(&extCmdProcessLock);
extCmdProcess = new QProcess(this);
QString argStr += " --p1=1"
+ " --p2=3";
extCmdProcess->start(cmd,argStr.split(QString(" ")));
bool startedSuccessfully = extCmdProcess->waitForStarted();
if (!startedSuccessfully) {
extCmdProcess->close();
extCmdProcess->kill();
extCmdProcess->waitForFinished();
delete extCmdProcess;
extCmdProcess = NULL;
return;
}
bool successfullyFinished = extCmdProcess->waitForFinished(-1);
if (!successfullyFinished) {
qDebug() << "finishing failed"; // Appendix C
extCmdProcess->close();
extCmdProcess->kill();
extCmdProcess->waitForFinished(-1);
delete extCmdProcess;
extCmdProcess = NULL;
return;
}
extCmdProcess->close();
delete extCmdProcess;
extCmdProcess = NULL;
}
DbManager::~DbManager(){
qDebug() << "DB DbManager destructor called.";
QMutexLocker locker(&extCmdProcessLock);
if (extCmdProcess!= NULL){
this->extCmdProcess->kill(); // added after Appendix A
this->extCmdProcess->waitForFinished();
}
}
Appendix A: 我也收到错误"QProcess: Destroyed while process is still running."并且我读到这可能意味着在waitForStarted()命令未完成时执行来自我的其他线程的"delete dbmanager"调用 . 但我真的想知道为什么我的析构函数中的kill()命令没有解决这个问题 .
Appendix B: 根据评论,添加 waitForFinished()
. 遗憾的是,QProcess终止仍然无法正常关闭,分段错误发生在 waitForStarted()
或 start()
本身 .
#0 0x00007f25e03a492a in QEventDispatcherUNIX::registerSocketNotifier () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#1 0x00007f25e0392d0b in QSocketNotifier::QSocketNotifier () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#2 0x00007f25e0350bf8 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#3 0x00007f25e03513ef in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#4 0x00007f25e03115da in QProcess::start () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#5 0x0000000000428628 in DbManager::extCmd()
#6 0x000000000042ca06 in DbManager::storePos ()
#7 0x000000000044f51c in DeviceConnection::incomingData ()
#8 0x00000000004600fb in DeviceConnection::qt_metacall ()
#9 0x00007f25e0388782 in QObject::event () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#10 0x00007f25e0376e3f in QCoreApplicationPrivate::notify_helper () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#11 0x00007f25e0376e86 in QCoreApplication::notify () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#12 0x00007f25e0376ba4 in QCoreApplication::notifyInternal () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#13 0x00007f25e0377901 in QCoreApplicationPrivate::sendPostedEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#14 0x00007f25e03a4500 in QEventDispatcherUNIX::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#15 0x00007f25e0375e15 in QEventLoop::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#16 0x00007f25e0376066 in QEventLoop::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#17 0x00007f25e0277715 in QThread::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#18 0x00007f25e027a596 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#19 0x00007f25df9b43f7 in start_thread () from /lib/libpthread.so.0
#20 0x00007f25def89b4d in clone () from /lib/libc.so.6
#21 0x0000000000000000 in ?? ()
Appendix C: 调试输出向我显示错误消息:QProcess:进程仍在运行时被销毁 . 出现完成失败输出时总是出现 . 这意味着我的锁或/和kill尝试保护QProcess失败 . 我想知道的问题:
a)如果创建一个QProcess对象并启动它,我的 extCmdProcessLock
是否已解锁?我已经尝试使用正常的 lock()
调用而不是 QMutexLoader
,但没有运气 .
b)文档说如果我以这种方式使用QProcess,主线程将被停止 . 它们真的是指主线程或启动QProcess的线程吗?我假设第二 .
c)QProcess是否在多线程环境中不可用?如果两个线程创建一个QProcess对象并运行它,它们会干扰吗?也许对象在某种程度上是静态的?
感谢您填写知识泄漏的任何帮助 . 我真的希望解决这个难题 .
Appendix D: 从任何线程中删除任何delete和deleteLater()后,我的QProcess仍然被粉碎 .
#0 0x00007fc94e9796b0 in QProcess::setProcessState () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#1 0x00007fc94e97998b in QProcess::waitForStarted () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#2 0x00007fc94e979a12 in QProcess::waitForFinished () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#3 0x0000000000425681 in DbManager::extCmd()
#4 0x0000000000426fb6 in DbManager::storePos ()
#5 0x000000000044d51c in DeviceConnection::incomingData ()
#6 0x000000000045fb7b in DeviceConnection::qt_metacall ()
#7 0x00007fc94e9f4782 in QObject::event () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#8 0x00007fc94e9e2e3f in QCoreApplicationPrivate::notify_helper () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#9 0x00007fc94e9e2e86 in QCoreApplication::notify () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#10 0x00007fc94e9e2ba4 in QCoreApplication::notifyInternal () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#11 0x00007fc94e9e3901 in QCoreApplicationPrivate::sendPostedEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#12 0x00007fc94ea10500 in QEventDispatcherUNIX::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#13 0x00007fc94e9e1e15 in QEventLoop::processEvents () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#14 0x00007fc94e9e2066 in QEventLoop::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#15 0x00007fc94e8e3715 in QThread::exec () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#16 0x00007fc94e8e6596 in ?? () from /usr/local/Trolltech/Qt-4.7.4/lib/libQtCore.so.4
#17 0x00007fc94e0203f7 in start_thread () from /lib/libpthread.so.0
#18 0x00007fc94d5f5b4d in clone () from /lib/libc.so.6
#19 0x0000000000000000 in ?? ()
2 回答
使用QThread来管理正在运行的进程真的很糟糕 . 我一次又一次地看到它,这是关于如何正确编写异步应用程序的一些基本误解 . 进程与您自己的应用程序分开 . QProcess提供了一组漂亮的信号,可在成功启动,启动失败和完成时通知您 . 只需将这些信号挂钩到您的QObject派生类的实例中的插槽中,您就可以全部设置 .
如果应用程序中的线程数量明显超过平台上可用的核心数/超线程数,或者线程数与某些不相关的运行时因子(如正在运行的子进程数)相关联,则设计很糟糕 .
看到我的其他other answer .
您可以在堆上创建QProcess,作为监视QObject的子级 . 您可以将QProcess的finished()信号连接到它自己的deleteLater()插槽,这样它就会在完成时自动删除 . 监视QObject应该强制终止任何剩余的正在运行的进程,当它被自己销毁时,比如应用程序关闭的结果 .
进一步的问题是如何执行不受控制的长时间运行的函数,比如没有异步API的数据库查询,影响最小,当穿插有良好异步API的东西时,例如QProcess .
规范的方法是:在必要的地方同步执行,否则异步执行 . 您可以通过调用其
deleteLater()
插槽来停止控制对象和任何正在运行的进程 - 通过信号/插槽连接,或者如果您想在安全地越过线程边界时直接执行QMetaObject::invokeMethod()
. 这是使用尽可能少的阻塞调用的主要好处:您可以控制处理并且可以在某些时候停止它 . 使用纯粹的阻塞实现,没有办法阻止它使用一些标志变量并将代码用于测试 .只要事件循环在QObject所在的线程中旋转,
deleteLater()
就会被处理 . 这意味着它将在数据库查询调用之间获得机会 - 实际上是在进程运行的任何时候 .未经测试的代码:
就个人而言,我会检查您使用的数据库是否具有异步API . 如果它没有,但如果客户端库有可用的源,那么我会做一个最小的端口来使用Qt的网络堆栈使其异步 . 它会降低开销,因为你不再需要每个数据库连接一个线程,并且当你接近使CPU饱和时,开销就不会上升:通常,要使CPU饱和,你需要很多很多线程,因为他们大多闲着 . 使用异步接口,上下文切换的数量会下降,因为线程将处理来自数据库的一个数据包,并且可以立即处理来自不同连接的另一个数据包,而不必进行上下文切换:执行保持在该线程的事件循环 .
QProcess::waitForStarted只是表示您的流程已经启动 . extCmd()方法中的互斥锁被解锁,因为您没有在此方法中等待QProcess::waitForFinished . 子进程仍在运行时,您将 exit 此方法 .
如果你想使用fire&forget类型的执行,我只使用QProcess::startDetached