Завершить текущий процесс QProcess, запущенный внутри QThread?
Как прекратить текущий процесс QProcess, который выполняется внутри QThread и удаляется другим QThread? Я даже вставил QMutex extCmdProcessLock, который должен избежать разрушения DbManager до того, как extCmdProcess может завершиться или истечет время ожидания. Я получаю ошибку сегментации на "waitForStarted", если другой поток вызывает удаление на DbManager. Я не могу использовать сигналы (я думаю), потому что я использую внешнюю команду внутри последовательного процесса обработки данных. Большое спасибо за любую помощь!
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: уничтожено во время работы процесса". и я прочитал, что это может означать, что вызов "delete dbmanager" из моего другого потока выполняется, пока команда waitForStarted() не выполнена. Но мне действительно интересно, почему команда kill() в моем деструкторе не исправила это.
Приложение Б: Согласно комментарию, добавлено 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: уничтожено, пока процесс еще запущен. всегда появляется, когда появляется окончательный неудачный вывод. Это означает, что мои попытки блокировки и / или уничтожения для защиты QProcess терпят неудачу. Вопросы, которые меня интересуют:
а) Если создать объект QProcess и запустить его, мой extCmdProcessLock
разблокирован? Я уже пытался использовать нормальный lock()
звоните вместо QMutexLoader
но не повезло.
б) Документы говорят, что основной поток будет остановлен, если я буду использовать QProcess таким образом. Они действительно означают основной поток или поток, в котором запущен QProcess? Я предположил второе.
в) 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 ?? ()
2 ответа
Это действительно плохой стиль - использовать QThread для управления запущенным процессом. Я вижу это снова и снова, и это фундаментальное недоразумение о том, как правильно писать асинхронные приложения. Процессы отделены от вашего собственного приложения. QProcess предоставляет прекрасный набор сигналов, чтобы уведомить вас об успешном запуске, сбое запуска и завершении. Просто подключите эти сигналы к слотам в вашем классе, производном от QObject, и все будет готово.
Это плохой дизайн, если количество потоков в вашем приложении может значительно превышать количество ядер / гипер-нитей, доступных на платформе, или если количество потоков связано с каким-то несвязанным фактором времени выполнения, таким как количество запущенных подпроцессов.
Смотрите мой другой ответ.
Вы можете создать QProcess в куче, как дочерний элемент вашего QObject мониторинга. Вы можете подключить сигнал FinPro () QProcess к своему собственному слоту 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)();
};
Лично я бы проверил, имеет ли используемая вами база данных асинхронный API. Если это не так, но если у клиентской библиотеки есть доступные источники, я бы сделал минимальный порт для использования сетевого стека Qt, чтобы сделать его асинхронным. Это снизило бы издержки, потому что у вас больше не было бы одного потока на соединение с базой данных, и когда вы приблизились бы к насыщению ЦП, накладные расходы не увеличились бы: обычно, чтобы насыщать ЦП, вам понадобится много-много темы, так как они в основном простаивают. При асинхронном интерфейсе число переключений контекста будет уменьшаться, поскольку поток будет обрабатывать один пакет данных из базы данных и может немедленно обработать другой пакет из другого соединения без необходимости переключения контекста: выполнение остается в пределах Цикл событий этого потока.
QProcess::waitForStarted просто сигнализирует о том, что ваш процесс запущен. Тогда мьютекс в методе extCmd() разблокируется, потому что вы не ожидаете QProcess::waitForFinished в этом методе. Вы выйдете из этого метода, пока дочерний процесс еще выполняется.
Если вы хотите использовать тип исполнения fire& забудь, я просто использую QProcess::startDetached