2016-06-24 114 views
1

main()つまりメインスレッドにQThreadを作成しました。 ワーカークラスを新しいスレッドに移動しました。スレッドは、ワーカークラスの 'StartThread'メソッドを実行します。Stop Qt Thread:exit()またはquit()を呼び出してもスレッドの実行が停止しない

ワーカースレッド: 'StartThread' 完了し、スレッドが正常に終了する前に

//header file 
class Worker : public QObject 
{ 
    Q_OBJECT 
public: 
    Worker(QThread* thread); 

public slots: 
    void StartThread(); 
    void EndThread(); 

private: 
    QThread* m_pCurrentThread; 
}; 


// source file 
#include "QDebug" 
#include "QThread" 
#include "worker.h" 

Worker::Worker(QThread* thread) 
{ 
m_pCurrentThread = thread; 
} 

void Worker::StartThread() 
{ 
    qDebug() << " StartThread"; 

    while(true) 
    { 
     QThread::msleep(1000); 
     qDebug() << " thread running"; 
     static int count = 0; 
     count++; 
     if(count == 10) 
     {    
      break; 
     } 
     if(m_pCurrentThread->isInterruptionRequested()) 
     { 
      qDebug() << " is interrupt requested"; 
      // Option 3: 
      m_pCurrentThread->exit();    
     } 
    } 
    qDebug() << "StartThread finished"; 
} 

void Worker::EndThread() 
{ 
    qDebug() << "thread finished"; 
} 

MAIN.CPP

#include <QCoreApplication> 
#include "worker.h" 
#include "QThread" 
#include "QObject" 
#include "QDebug" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    QThread* thread = new QThread(); 
    Worker* workerObj = new Worker(thread); 

    QObject::connect(thread, 
        SIGNAL(started()), 
        workerObj, 
        SLOT(StartThread())); 
    QObject::connect(thread, 
        SIGNAL(finished()), 
        workerObj, 
        SLOT(EndThread())); 



    workerObj->moveToThread(thread); 
    thread->start(); 
    thread->requestInterruption(); 
    QThread::msleep(2000); 
    qDebug() << "terminate thread"; 
    if(thread->isRunning()) 
    { 
     // Option 1,2 exit()/quit() used but got same output 
     thread->exit(); 
     qDebug() << "wait on thread"; 
     thread->wait(); 
    } 
    qDebug() << " exiting main"; 

    return a.exec(); 
} 

は、今私はメインスレッドからスレッドを停止します。 が使用される、

  1. スレッド>の出口() メインスレッドで(スレッド>待つ())を待っていました。
  2. thread-> quit() メインスレッドの待機(thread-> wait())。

  3. の出口()の 'StartThread'

予想:は スレッドの実行が()/終了終了するとすぐに停止する必要があります()メインスレッドから呼び出されます。

は実際: スレッドが稼働し続け、すべての3つのインスタンスでは、「StartThread」メソッドを完了し、正常に終了します。 exit()/ quit()が呼び出されていない場合と同様に良好です。 出力:

StartThread 
thread running 
is interrupt requested 
terminate thread 
wait on thread 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
is interrupt requested 
thread running 
StartThread finished 
thread finished 
exiting main 

はそれがメインスレッドで必要とされるときのようにスレッドを配置/停止することは可能ですか?

答えて

-2

countを宣言して初期化しました。whileループ内にあります。それを外側に移動すると、すべての反復がゼロに再初期化されなくなり、時間通りに終了します。私はあなたがなぜそれをstaticと宣言したのかもわかりません。


あなたが定期的にあなたのwhileループ内のスレッドのイベントディスパッチャにprocessEventsを呼び出すことを確認し、キューに入れられた信号を使用してイベントを処理する場合:

if (thread()->eventDispatcher()->hasPendingEvents() 
    thread()->eventDispatcher()->processEvents(); 
3

isInterruptionRequestedだけ君場合にtrueを返しますQThread::requestInterruptionと、とは呼ばず、QThread::quitと呼ばれています。これは文書化されています。

QThread::quitを使用する場合、スレッドはイベントループを回転させる必要があります。したがって、スレッドから派生してはなりません。代わりにこれを実行してください:

class Worker : public QObject { 
    QBasicTimer m_timer; 
    int chunksToDo = 20; 
    void doChunkOfWork() { 
    QThread::sleep(1); 
    } 
    void timerEvent(QTimerEvent * ev) { 
    if (ev->timerId() != m_timer.timerId()) return; 
    if (!chunksToDo) { m_timer.stop(); return; } 
    doChunkOfWork(); 
    if (!--chunksToDo) emit done(); 
    } 
public: 
    explicit Worker(QObject * parent = nullptr) : QObject{parent} { 
    m_timer.start(0, this); 
    } 
    Q_SIGNAL void done(); 
}; 

ワーカーを実行するには、ワーカーを作成してそれをスレッドに移動します。ワーカーを停止するには、ワーカーが完了する前にそのスレッドだけをquit()にします。

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    Worker worker; 
    QThread thread; 
    worker.moveToThread(&thread); 
    QObject::connect(&worker, &Worker::done, &thread, &QThread::quit); 
    QObject::connect(&thread, &QThread::finished, &app, &QCoreApplication::quit); 
    thread.start(); 
    return app.exec(); 
} 

スレッドの寿命を手動で管理する代わりに、代わりにスレッドプールを使用することもできます。

class Worker : public QObject, QRunnable { 
    int chunksToDo = 20; 
    volatile bool active = true; 
    void doChunkOfWork() { 
    QThread::sleep(1); 
    } 
    void run() { 
    while (active && chunksToDo) { 
     doChunkOfWork(); 
     --chunksToDo; 
    } 
    emit done(); 
    } 
public: 
    using QObject::QObject; 
    Q_SLOT void stop() { active = false; } 
    Q_SIGNAL void done(); 
}; 

int main(int argc, char ** argv) { 
    QCoreApplication app{argc, argv}; 
    QThreadPool pool; 
    Worker worker; 
    worker.setAutoDelete(false); // important! 
    pool.start(&worker); 
    // * 
    QTimer::singleShot(5000, &worker, &Worker::stop); 
    QObject::connect(&worker, &Worker::done, &app, &QCoreApplication::quit); 
    return app.exec(); 
} 

メインイベントループを実行せずに、それを終了する別の方法があったであろう:

// * 
    QThread::sleep(5); 
    worker.stop(); 
    pool.waitForDone(); 
} 

This answerは、スレッド・プールに複数のジョブをストリーミングの完全な例があります。

1

多くの誤解があるようです。 Qtのドキュメント、QThread::quit()QThread::exit()の両方から

があれば、スレッドのイベントループが実行されていない(どちらかQThread::exec()がされていないことを意味することに

を終了するには、スレッドのイベントループに知らせます(あなたのStartThreadのような)重いブロッキング機能を実行しているイベントループビジーの場合)、スレッドはコントロールがイベントループに戻るまで終了しません。私はそれがなぜquit()exit()があなたのために期待どおりに動かなかったのかを説明すると思います。

また、あなたが戻ってQt docsに、間違った方法でQThread::requestInterruption()を使用しているように見える:

は、スレッドの中断を要求します。その要求は勧告であり、その要求に応じてどのように動作するかを決定するのはスレッド上で実行されるコードに依存します。この関数は、スレッド上で実行されているイベントループを停止せず、終了もしません。

あなたのスレッドですべてのことを行うあなたは(スレッド内で)あなたはできるだけ早くクリーンアップを実行し、終了する必要があることを検出したときにようにこれは本当に、それだけで、trueを返すようにQThread::isInterruptionRequested()への後続の呼び出しが発生していません可能であれば(ここでもquit()を動作させるには、イベントループが実行されている必要がありますので、ここではStartThread関数から戻り、スレッドを再度イベントループに戻す必要があります)。あなたの質問に答えるために今

はそれがメインスレッドで必要とされるときのようにスレッドを配置/停止することは可能ですか?

それを行うには、あなたは、このような長時間実行される機能を回避しquit()/exit()が働くことができるように、あなたは、できるだけ早くイベントループに戻ることを確認する必要がありどちらか。または、isInterruptionRequested()がtrueを返すことを検出するとすぐに、現在の関数からクリーンアップと戻りを実行します。その結果、quit()/exit()への呼び出しがその後で機能するようになります。

@kubaによって指摘されているように、スレッドの寿命を手動で管理する代わりに、スレッドプールを使用することもできます。

P.S.isInterruptionRequested()を使用するためにへのポインタをWorkerの内部に格納する必要はありません。 m_pCurrentThread->isInterruptionRequested()の代わりにQThread::currentThread()->isInterruptionRequested()を使用できます(出口電話m_pCurrentThread->isInterruptionRequested()にも同じことが適用されます)

関連する問題