Завершить текущий процесс 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

Другие вопросы по тегам