2017-01-11 33 views
3

私はバックグラウンドでイメージ取得を行うワーカークラスを持っています。QThreadを正しく終了します

void acq::run() 
{ 
    while (m_started) 
    { 
     blocking_call(); 
    } 
    emit workFinished(); 
} 

void acq::start() 
{ 
    m_started = true; 
    run(); 
} 

void acq::stop() 
{ 
    m_started = false; 
} 

start(); stop()はスロットで、workFinishedは信号です。

は、だから私のUIクラスでは、私は労働者を起動すると、私はスロットに信号を接続します

m_thread = new QThread; 
m_worker = new acq(); 

m_worker->moveToThread(m_thread); 

// When the thread starts, then start the acquisition. 
connect(m_thread, SIGNAL (started()), m_worker, SLOT (start())); 

// When the worker has finished, then close the thread 
connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit())); 

m_thread->start(); 

この時点で、私はスロット、closeEvent

void UIClass::closeEvent (QCloseEvent *event) 
{ 
    m_worker->stop(); // tell the worker to close 
    m_thread->wait(); // wait until the m_thread.quit() function is called 
    event->accept(); // quit the window 
} 

Unfortanely、m_thread->wait()を実装しましたブロックしています。信号quit()

おかげ

編集emmitedされている場合でも:acq::~acq()

connect(m_worker, SIGNAL(workFinished()), m_worker, SLOT(deleteLater())); 
connect(m_thread, SIGNAL(finished()), m_thread, SLOT(deleteLater())); 

とQdebugメッセージを証明するように印刷されています

を私はこれらの2つの接続を追加しましたそのストップが呼び出され、workFinishedが送出され、deleteLater()が送出されます。

+0

デバッガを使用して、待機しているスレッドが実際に終了したことを確認しましたか?終了していない場合はどうしていますか? – UKMonkey

答えて

0

Ralph TandetzkyとKevin Krammerの助けを借りて、私はついに解決策を見つけました。

  • 代わりm_worker->stop();でスレッドを閉じて、私は労働者のイベントループにQMetaObject::invokeMethod(m_worker, "stop", Qt::ConnectionType::QueuedConnection);QCoreApplication::processEvents();を使用しています。行動は変わらないが、競争状態やその他の問題を防ぐことを望む。代わりに使用したの

  • connect(m_worker, SIGNAL(workFinished()), m_thread, SLOT(quit()));、私は、カスタムスロットを使用します。

    connect(m_worker, &Acq::workFinished, [=] 
    { 
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
        QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection); 
    }); 
    

    我々は無限ループの外にあるので、DirectConnectionを使用するので、イベントは処理されません。

  • これで、私は最後の問題がありました。 m_thread->waitがブロックされています。イベントを読む必要があります。そうしないと、カスタムスロットが呼び出されません。そこで、イベントループをUIクラスQEventLoop m_loopに追加しました。
    ただm_thread->wait()前に、私はm_loop.exec(); を書き、m_loop.quit()が呼び出される終了するまで最終的には、私のカスタムスロットでは、私はm_loop.quit()

    connect(m_worker, &Acq::workFinished, [=] 
    { 
        std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
        QMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection); 
        m_loop.quit(); 
    }); 
    

    m_loop.exec()プロセスイベントを置きます。この方法ではm_thread->wait()も必要ありません。workFinishedが発行されたときにm_loop.quit()が呼び出されるためです。私はQMetaObject::invokeMethod(m_thread, "quit", Qt::ConnectionType::DirectConnection);もう

は、今では魔法のように動作する必要はありません

EDIT:このソリューションは非常に重く、醜いです、Qtの(https://www.qtdeveloperdays.com/sites/default/files/David%20Johnson%20qthreads.pdfが)私の場合には、サブクラスとrequestInteruptionを使用するsugest。

3

異なるスレッド上のオブジェクト間の通常の信号/スロット接続では、受信側オブジェクトのスレッドがイベントループを実行する必要があります。

理論的には受信側スレッドはrun()が返されないので、イベントループはstart()スロットを実行してビジー状態です。

レシーバイベントループのブロックを解除するか、Qt::DirectConnectionのストップスロットを呼び出す必要があります。

後者の場合、スロットが送信側スレッドのコンテキストで呼び出され、同時アクセスに対してm_startedを保護する必要があることに注意する必要があります。あなたはQThread::requestInterruption()QThread::isInterruptionRequested()

+0

お返事ありがとうございます。私はいくつか質問があります: - 異なるスレッド上のオブジェクト間の通常の信号/スロット接続では、受信側オブジェクトのスレッドがイベントループを実行する必要があります。レシーバーはウィーカーの権利ですか? - しかし、run()は返されないので、イベントループはstart()スロットを実行してビジーです。なぜ、ワーカーがビジー状態であればstop()関数のqDebugが呼び出されますか?例はありますか? – Epitouille

+0

@Epitouille、あなたの場合に 'stop()'スロットが呼び出されていますか? – Mike

+0

@Mikeはいそれは – Epitouille

1

があなたのループに

QCoreApplication::processEvents(); 

を追加し、それがうまくいく使用することができ、独自のフラグを使用する代わりに

デッドロックする理由は、acq::run()への呼び出しがブロックされ、ワー​​カースレッドでacq::stop()が実行される時間がないことです。

+0

お返事ありがとうございます。私はQCoreApplication :: processEvents(QEventLoop :: WaitForMoreEvents)とは思わない。解決策です。 acq :: run()ループにこの行を追加するとスレッドがブロックされます。この行には労働者がいません。しかし、この関数(スロット)のmy qDebug()が表示されるため、acq :: stop()が呼び出されます。 – Epitouille

+0

@Epitouille 'QObject :: invoke'で' acq :: stop() 'スロットを使いましたか? –

+0

はいm_worker-> stop()をQMetaObject :: invokeMethod(m_worker、 "stop"、Qt :: ConnectionType :: DirectConnection)に変更しました。 – Epitouille

関連する問題