1

QThread 内で実行され、別の QThread によって削除される進行中の QProcess を終了する方法は? QMutex extCmdProcessLock も挿入しました。これにより、extCmdProcess が終了またはタイムアウトする前に DbManager が破壊されるのを回避できます。別のスレッドが DbManager で削除を呼び出すと、「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: Destroyed while process is still running.」というエラーも表示されます。そして、これは、waitForStarted() コマンドが完了していない間に、他のスレッドからの「delete dbmanager」呼び出しが実行されることを意味する可能性があることを読みました。しかし、デストラクタの kill() コマンドでこれが修正されなかったのはなぜだろうか。

付録 B:コメントによると、追加されましwaitForFinished()た。waitForStarted()悲しいことに、QProcess の終了はまだ適切にシャットダウンされません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: Destroyed while process is still running. 終了失敗出力が表示された場合は、常に表示されます。これは、QProcess を保護するためのロックまたは強制終了の試みが失敗していることを意味します。気になる質問:

a) QProcess オブジェクトを作成して開始すると、ロックがextCmdProcessLock解除されますか? lock()の代わりに通常の呼び出しを使用しようとしましたQMutexLoaderが、うまくいきませんでした。

b)この方法で QProcess を使用すると、メイン スレッドが停止するというドキュメントがあります。それらは本当にメインスレッドまたはQProcess が開始されるスレッドを意味しますか? 私は2番目だと思いました。

c) QProcess はマルチスレッド環境では使用できませんか? 2 つのスレッドが 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 ?? ()
4

2 に答える 2

6

QThread を使用して実行中のプロセスを管理するのは、非常に悪いスタイルです。私はこれを何度も目にしていますが、それは非同期アプリケーションを適切に作成する方法についての根本的な誤解です。プロセスは、独自のアプリケーションから分離されています。QProcess は、正常に開始されたとき、開始に失敗したとき、および終了したときに通知する美しい一連のシグナルを提供します。これらのシグナルを QObject から派生したクラスのインスタンスのスロットにフックするだけで、準備は完了です。

アプリケーションのスレッド数がプラットフォームで利用可能なコア/ハイパースレッドの数を大幅に超える可能性がある場合、またはスレッドの数が実行中のサブプロセスの数などの無関係なランタイム要因にリンクされている場合は、設計が不適切です。

私の他の他の答えを見てください。

モニタリング QObject の子として、ヒープ上に QProcess を作成できます。QProcess の finished() シグナルを独自の deleteLater() スロットに接続して、完了時に自動的に削除されるようにすることができます。監視している QObject は、たとえばアプリケーションのシャットダウンの結果としてそれ自体が破壊された場合、残りの実行中のプロセスを強制的に終了する必要があります。

さらに問題は、QProcess などの適切な非同期 API が散在している場合に、非同期 API がないデータベース クエリなど、制御不能なほど長時間実行される機能を実行する方法でした。

標準的な方法は次のとおりです。必要な場合は同期的に処理し、そうでない場合は非同期的に処理します。制御オブジェクトと実行中のプロセスを停止するにはdeleteLater()、シグナル/スロット接続を介して、またはQMetaObject::invokeMethod()スレッド境界を安全に越えながら直接実行したい場合は を使用して、そのスロットを呼び出します。これは、ブロッキング呼び出しをできるだけ少なく使用することの主な利点です。処理をある程度制御でき、時々停止することができます。純粋にブロック実装では、いくつかのフラグ変数を使用し、コードにそのためのテストをまき散らす以外にそれを止める方法はありません。

イベントループがQObjectdeleteLater()が存在するスレッドでスピンできるときはいつでも処理されます。これは、データベース クエリ呼び出しの間にチャンスが得られることを意味します。実際には、プロセスが実行されているときはいつでもです。

テストされていないコード:

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 のネットワーク スタックを使用して非同期にする最小限のポートを実行します。データベース接続ごとに 1 つのスレッドがなくなるため、オーバーヘッドが低下します。また、CPU の飽和に近づくにつれて、オーバーヘッドは上昇しません。通常、CPU を飽和させるには、非常に多くのスレッドが必要になります。スレッドはほとんどアイドル状態であるためです。非同期インターフェイスでは、スレッドがデータベースからのデータの 1 つのパケットを処理し、コンテキスト スイッチを実行することなく、別の接続からの別のパケットをすぐに処理できるため、コンテキスト スイッチの数が減少します。そのスレッドのイベントループ。

于 2012-05-31T22:06:50.760 に答える
1

QProcess::waitForStartedは、プロセスが開始されたことを通知するだけです。このメソッドでQProcess::waitForFinishedを待っていないため、extCmd() メソッドのミューテックスはロック解除されます。子プロセスがまだ実行されている間に、このメソッドを終了します。

fire&forget タイプの実行を使用する場合は、 QProcess::startDetachedを使用するだけです

于 2012-05-28T06:28:14.970 に答える