spi*_*key 1 linux qt multithreading qprocess
如何终止在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();
}
}
Run Code Online (Sandbox Code Playgroud)
附录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 ?? ()
Run Code Online (Sandbox Code Playgroud)
附录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 ?? ()
Run Code Online (Sandbox Code Playgroud)
使用QThread来管理正在运行的进程真的很糟糕.我一次又一次地看到它,这是关于如何正确编写异步应用程序的一些基本误解.进程与您自己的应用程序分开.QProcess提供了一组漂亮的信号,可在成功启动,启动失败和完成时通知您.只需将这些信号挂钩到您的QObject派生类的实例中的插槽中,您就可以全部设置.
如果应用程序中的线程数量明显超过平台上可用的核心数/超线程数,或者线程数与某些不相关的运行时因子(如正在运行的子进程数)相关联,则设计很糟糕.
看到我的其他答案.
您可以在堆上创建QProcess,作为监视QObject的子级.您可以将QProcess的finished()信号连接到它自己的deleteLater()插槽,这样它就会在完成时自动删除.监视QObject应该强制终止任何剩余的正在运行的进程,当它被自己销毁时,比如应用程序关闭的结果.
进一步的问题是如何执行不受控制的长时间运行的函数,比如没有异步API的数据库查询,影响最小,当穿插有良好异步API的东西时,例如QProcess.
规范的方法是:在必要的地方同步执行,否则异步执行.您可以通过调用其deleteLater()插槽来停止控制对象和任何正在运行的进程- 通过信号/插槽连接,或者QMetaObject::invokeMethod()如果您希望在安全地跨越线程边界时直接执行它.这是使用尽可能少的阻塞调用的主要好处:您可以控制处理并且可以在某些时候停止它.使用纯粹的阻塞实现,没有办法阻止它使用一些标志变量并将代码用于测试.
在deleteLater()将得到处理的任何时间,事件循环可以在一个QObject住螺纹旋转.这意味着它将在数据库查询调用之间获得机会 - 实际上是在进程运行的任何时候.
未经测试的代码:
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)();
};
Run Code Online (Sandbox Code Playgroud)
就个人而言,我会检查您使用的数据库是否具有异步API.如果没有,但如果客户端库有可用的源,那么我会做一个最小的端口来使用Qt的网络堆栈使其异步.它会降低开销,因为你不再需要每个数据库连接一个线程,并且当你接近使CPU饱和时,开销就不会上升:通常,要使CPU饱和,你需要很多很多线程,因为他们大多闲着.使用异步接口,上下文切换的数量会下降,因为线程将处理来自数据库的一个数据包,并且可以立即处理来自不同连接的另一个数据包,而无需进行上下文切换:执行保持在该线程的事件循环.