QThreadのコンテキストでメソッドを呼び出す
-
06-07-2019 - |
質問
私のアプリケーションには、メインスレッドとワーカースレッド( QThread
)があります。
メインスレッドから、ワーカースレッドのメソッドを呼び出して、スレッドのコンテキストで実行させます。
QMetaObject :: invokeMethod
を使用して QueuedConnection
オプションを指定しようとしましたが、機能しません。
また、メインスレッド(ワーカースレッドのスロットに接続されている)から信号を発信しようとしましたが、それも失敗しました。
これは私が試したおおよそのスニペットです:
class Worker : public QThread
{
Q_OBJECT
public:
Worker() { }
void run()
{
qDebug() << "new thread id " << QThread::currentThreadId();
exec();
}
public slots:
void doWork()
{
qDebug() << "executing thread id - " << QThread::currentThreadId();
}
};
QMetaObjectの方法の使用:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id - " << QThread::currentThreadId();
Worker worker;
worker.start();
QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);
return a.exec();
}
信号方式の使用:
class Dummy : public QObject
{
Q_OBJECT
public:
Dummy() { }
public slots:
void askWork() { emit work(); }
signals:
void work();
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id - " << QThread::currentThreadId();
Worker worker;
worker.start();
Dummy dummy;
QObject::connect(&dummy, SIGNAL(work()), &worker, SLOT(doWork()), Qt::QueuedConnection);
QTimer::singleShot(1000, &dummy, SLOT(askWork()));
return a.exec();
}
どちらの方法でも、メインスレッドIDは QThread
doWork
に出力されます。
また、私は単純なプロデューサー/コンシューマーを実装することを考えましたが、これが機能する場合、このようにしない理由はありますか?
解決
問題は、レシーバー(QThread)がメインスレッドで「生きている」ため、メインスレッドのイベントループがスロットを実行するループであるということでした。
Qtのドキュメントから:
キュー接続では、オブジェクトが属するスレッドのイベントループに制御が戻ると、スロットが呼び出されます。スロットは、レシーバーオブジェクトが存在するスレッドで実行されます。
したがって、これまでに見つけた解決策は、スレッドのrun()内にオブジェクトを作成し、代わりにそのスロットを使用することでした。そうすれば、受信者の所有者がスレッドになり、スレッドコンテキストでスロットが呼び出されます。
他のヒント
この例は、Workerクラスを分割して、希望どおりに機能させる方法を示しています。また、スロットに接続できるようにするには、Workerインスタンスへの参照またはポインターを使用可能にする必要があります。
class Worker : public QObject
{
Q_OBJECT
public:
Worker() { }
public slots:
void doWork()
{
qDebug() << "executing thread id - " << QThread::currentThreadId();
}
};
class WorkerThread : public QThread
{
Q_OBJECT
public:
void run()
{
qDebug() << "new thread id " << QThread::currentThreadId();
Worker worker;
exec();
}
};
単純なプロデューサー/コンシューマーの例については、Bradley T. Hughesのブログエントリをご覧ください頭痛のない歩行。
ワーカーはメインスレッドで作成されるため、イベントはメインスレッドで処理されます。ワーカーを独自のスレッドに移動する必要があります。
Worker worker;
worker.moveToThread(&worker);
worker.start();
Qtは、 worker
が新しいスレッドに存在することを認識し、そのイベントループのイベントをキューに入れます。
関数を呼び出したり、シグナルを送信する前に、ワーカースレッドが完了したように見えます。