つくねの手帳

C++およびAndroidアプリ開発メインで何か書きたい

QThreadとWorkerクラスの後処理

Qtでの非同期処理はQThreadクラスと自前のWorkerクラスで以下のように実装できる。

Worker.h

class Worker : public Qobject
{
    Q_OBJECT
public:
    Worker(); // 親を指定してしまうとスレッドに渡せなくなる。
    ~Worker();

public slots:
    // 作業用スロット

};

main.cpp

QThread* workerThread = new QThread(this);
Worker* worker = new Worker();

worker->moveToThread(workerThread);

connect(workerThread , SIGNAL(finished()) , workerThread , SLOT(deleteLater)):
connect(workerThread , SIGNAL(finished()) , worker , SLOT(deleteLater)):
// 作業用スロットのコネクト

workerThread->start();

スレッドに処理を依頼するときは、作業用スロットに対してシグナルを送ってあげることで実行できる。

アプリ終了時等でスレッドを止める際は、

workerThread->quit();

とすることで、QThreadクラスないでfinishedシグナルが発行され、workerクラスとスレッドがdeleteされる。


単発的な処理(ライフサイクルの短いスレッド)の場合は以下のように実装することで、外からQThreadのquit()を呼ぶことなく並列処理を実行できる。

Worker.h

public slots:
    void process();
signals:
    void processEnd();

Worker.cpp

void Worker::process()
{
    // 何かしらの処理

    // 処理終了
    emit this->processEnd():
}

main.cpp

QThread* workerThread = new QThread(this);
Worker* worker = new Worker();

worker->moveToThread(workerThread);

connect(workerThread , SIGNAL(started()) , worker , SLOT(process()));
connect(worker , SIGNAL(processEnd()) , workerThread , SLOT(quit()));
connect(worker , SIGNAL(processEnd()) , worker , SLOT(deleteLater()));
connect(workerThread , SIGNAL(finished()) , workerThread , SLOT(deleteLater()));

workerThread->start();

この場合、startが呼ばれたところからworkerクラスの処理が始まり、終了と同時に、workerクラスのprocessEnd→workerThreadのquit、それぞれのdeleteLaterがコールされ処理が完結する。

この流れをアプリ終了時等、メインスレッド側のデストラクタで呼び出すとスレッドのdeleteLaterによるスレッド削除が実行される前にデストラクタを抜けてしまうようで、QtCreatorのアプリケーション出力に"QThread: Destroyed while thread is still running"と警告される。

QThreadのquit始動でworkerクラスをdeleteする場合は問題ないので、何かしらの解決策がありそうだが、見つかっていない。