如何终止在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();
}
}
附录A:我还收到错误“QProcess:进程仍在运行时被销毁”。我读到这可能意味着在waitForStarted()命令尚未完成时执行来自我的其他线程的“delete dbmanager”调用。但我真的很想知道为什么我的析构函数中的kill()命令没有解决这个问题。
附录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 ?? ()
附录C:调试输出向我显示错误消息: QProcess:在进程仍在运行时被销毁。总是出现,当完成失败时输出出现。这意味着我的锁或/和kill尝试保护QProcess失败。 我想知道的问题:
a)如果创建一个QProcess对象并启动它,我的extCmdProcessLock
是否已解锁?我已经尝试使用普通lock()
来代替QMutexLoader
,但没有运气。
b)文档说如果我以这种方式使用QProcess,主线程将被停止。它们是否真的意味着主线程或启动QProcess的线程?我假设第二。
c)QProcess是否在多线程环境中不可用?如果两个线程创建一个QProcess对象并运行它,它们会干扰吗?也许对象在某种程度上是静态的?
感谢您填写知识泄漏方面的任何帮助。我真的希望解决这个难题。
附录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 ?? ()
答案 0 :(得分:6)
使用QThread来管理正在运行的进程真的很糟糕。我一次又一次地看到它,这是关于如何正确编写异步应用程序的一些基本误解。进程与您自己的应用程序分开。 QProcess提供了一组漂亮的信号,可在成功启动,启动失败和完成时通知您。只需将这些信号挂钩到您的QObject派生类的实例中的插槽中,您就可以全部设置。
如果应用程序中的线程数量明显超过平台上可用的核心数/超线程数,或者线程数与某些不相关的运行时因素(如正在运行的子进程数)相关联,那么设计很糟糕。
查看我的其他other answer。
您可以在堆上创建QProcess,作为监视QObject的子级。您可以将QProcess的finished()信号连接到它自己的deleteLater()插槽,这样它就会在完成时自动删除。监视QObject应该在它自身被破坏时强行终止任何剩余的正在运行的进程,比如因为你的应用程序关闭了。
进一步的问题是如何执行不受控制的长时间运行的函数,比如没有异步API的数据库查询,影响最小,当穿插有良好异步API的东西时,例如QProcess。
一种规范的方式是:在必要的地方同步做事,否则就是异步。您可以通过调用其deleteLater()
插槽来停止控制对象和任何正在运行的进程 - 通过信号/插槽连接,或者如果您想在安全地跨越线程时直接执行它,请使用QMetaObject::invokeMethod()
边界。这是使用尽可能少的阻塞调用的主要好处:您可以控制处理并且可以在某些时候停止它。使用纯粹的阻塞实现,没有办法阻止它使用一些标志变量并将代码用于测试。
只要事件循环在QObject所在的线程中旋转,deleteLater()
就会得到处理。这意味着它将在数据库查询调用之间获得机会 - 实际上是在进程运行的任何时候。
未经测试的代码:
class Query : public QObject
{
Q_OBJECT
public:
Query(QObject * parent = 0) : QObject(parent) {
connect(process, SIGNAL(error(QProcess::ProcessError)), SLOT(error()));
connect(process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(finished(int,QProcess::ExitStatus)));
}
~Query() { process.kill(); }
void start() {
QTimer::singleShot(0, this, SLOT(slot1()));
}
protected slots:
void slot1() {
// do a database query
process.start(....);
next = &Query::slot2;
}
protected:
// slot2 and slot3 don't have to be slots
void slot2() {
if (result == Error) {...}
else {...}
// another database query
process.start(...); // yet another process gets fired
next = &Query::slot3;
}
void slot3() {
if (result == Error) {...}
deleteLater();
}
protected slots:
void error() {
result = Error;
(this->*next)();
}
void finished(int code, QProcess::ExitStatus status) {
result = Finished;
exitCode = code;
exitStatus = status;
(this->*next)();
}
private:
QProcess process;
enum { Error, Finished } result;
int exitCode;
QProcess::ExitStatus exitStatus;
void (Query::* next)();
};
就个人而言,我会检查您使用的数据库是否具有异步API。如果没有,但如果客户端库有可用的源,那么我会做一个最小的端口来使用Qt的网络堆栈使其异步。它会降低开销,因为你不再需要每个数据库连接一个线程,并且当你接近使CPU饱和时,开销就不会上升:通常,要使CPU饱和,你需要很多很多线程,因为他们大多闲着。使用异步接口,上下文切换的数量会下降,因为线程将处理来自数据库的一个数据包,并且可以立即处理来自不同连接的另一个数据包,而无需执行上下文切换:执行保持在该线程的事件循环。
答案 1 :(得分:1)
QProcess::waitForStarted只表示您的流程已经启动。 extCmd()方法中的互斥锁被解锁,因为您没有在此方法中等待QProcess::waitForFinished。当子进程仍在运行时,您将退出此方法。
如果您想使用火灾并忘记执行类型我只使用QProcess::startDetached