1。概要
Qt では、バックグラウンドタスク、非同期操作、マルチタスクなどの機能を実装するためにマルチスレッドも広く使用されています。マルチスレッドを利用することで、プログラムの応答性やユーザーエクスペリエンスが向上し、プログラムの動作効率や安定性も向上します。
その中で、QThread はマルチスレッドで最もよく使用されており、スレッド クラスを作成および制御するための 2 つのメソッドを提供します。
- QThread サブクラスを継承し、サブクラスに run() メソッドを実装します。
- QObject::moveToThread() メソッドを使用する
この記事では、QObject の子オブジェクトを別のスレッドに移動するメカニズムである moveToThread() メソッドに焦点を当てます。これにより、オブジェクトはシグナルとスロットを別のスレッドで処理でき、メインスレッドを他のタスクのために解放できます。
2. 方法の説明
thread を使用するWorkerControllerクラスでは、 QThreadオブジェクトとWorkerクラス オブジェクトを作成し、moveToThread () メソッドを使用してWorkerオブジェクトのすべてのイベント ループをQThreadオブジェクトに渡して処理する必要があります。
// 创建 Worker 对象
Worker* worker = new Worker;
// 创建一个新的线程
QThread* _workerThread = new QThread;
// 将Worker对象移动到新线程
worker ->moveToThread(_workerThread );
// 连接Worker对象的信号和槽
// 该线程结束时销毁
connect(_workerThread, &QThread::finished, worker, &QObject::deleteLater);
// 线程结束后发送信号,对结果进行处理
connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
// 启动新线程
_workerThread ->start();
Worker オブジェクトにスロット関数がある場合、これらのスロット関数は新しいスレッドで実行されます。以下のクラス図の dowork() スロット関数は新しいスレッドで実行されます。
3. コード:
まず、doWorkスロット関数に焦点を当てたワーク クラスを作成します。この関数は、スレッドが実行する必要がある作業を定義し、スロット関数をトリガーするためにスレッドにシグナルを送信する必要があります。
//
// Worker.h
//
#ifndef DEL_WORKER_H
#define DEL_WORKER_H
#include <QObject>
#include <QDebug>
#include <QThread>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
public slots:
void doWork(int parameter); // doWork 定义了线程要执行的操作
signals:
void resultReady(const int result); // 线程完成工作时发送的信号
};
#endif //DEL_WORKER_H
//
// Worker.cpp
//
#include "worker.h"
Worker::Worker(QObject *parent)
: QObject(parent)
{
}
void Worker::doWork(int parameter) {
qDebug() << __FUNCTION__ << " Thread ID: " << QThread::currentThreadId() << "\n";
emit resultReady(parameter);
}
次に、WorkerController クラスを定義します。このクラスは、ワーカー オブジェクトのイベント ループ作業を処理するための QThread オブジェクトを定義します。
#ifndef WORKERCONTROLLER_H
#define WORKERCONTROLLER_H
//
// WorkerController .h
//
#include <QThread>
#include <QDebug>
#include "worker.h"
// WorkerController 用于 启动子线程 和 处理子线程执行的结果
class WorkerController : public QObject
{
Q_OBJECT
public:
explicit WorkerController(QObject *parent = nullptr);
~WorkerController() override ;
public slots:
static void handleResults(int result); // 处理子线程执行的结果
signals:
void operate(const int); // 发送信号,触发线程
private:
QThread* _workerThread = nullptr;
};
#endif // WORKERCONTROLLER_H
//
// WorkerController.cpp
//
/* 在 构造函数中创建 worker 对象,并且将其事件循环全部交给_workerThread 对象来处理,最后启动该线程,然后触发其事件处理函数。*/
#include "workercontroller.h"
/* 在 构造函数中创建 worker 对象,并且将其事件循环全部交给_workerThread 对象来处理,最后启动该线程,然后触发其事件处理函数。*/
WorkerController::WorkerController(QObject *parent) : QObject(parent) {
//moveToThread 通用流程:
auto *worker = new Worker ;
_workerThread= new QThread(this);
worker->moveToThread(_workerThread);
// operate 信号发射后启动线程工作
connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));
// 该线程结束时销毁
connect(_workerThread, &QThread::finished, worker, &QObject::deleteLater);
// 线程结束后发送信号,对结果进行处理
connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
// 启动线程
_workerThread->start();
// 发射信号,开始执行
qDebug() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << '\n' ;
emit operate(9);
}
// 析构函数中调用 quit() 函数结束线程
WorkerController::~WorkerController() {
_workerThread->quit();
_workerThread->wait();
}
void WorkerController::handleResults(const int result) {
qDebug() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << ", last result: " << result;
}
次は main 関数です。main 関数では、新しいコントローラー オブジェクトを作成し、実行を開始します。
#include <QCoreApplication>
#include <QThread>
#include "workercontroller.h"
int main(int argc, char *argv[])
{
qDebug() << __FUNCTION__ << " Thread ID:" << QThread::currentThreadId() << '\n' ;
QCoreApplication a(argc, argv);
WorkerController c ;
return a.exec();
}
4. 走行結果
doWork スロット関数の新しいスレッド
5. 注意事項
moveToThread関数を使用する場合は、次の点に注意する必要があります。
- moveToThread 関数を使用できるのは QObject オブジェクトのみであり、他のオブジェクトは使用できません。
- moveToThread 関数が呼び出されると、このオブジェクトのスレッド コンテキストが変更されるため、この関数を呼び出した後は、このオブジェクトが属するスレッド コンテキストは使用できなくなります。
- オブジェクトが関数を実行していて、その関数がスレッド ロックを使用していない場合、その関数はオブジェクトの移動後も元のスレッドで実行されます。したがって、オブジェクトを移動する前に、そのオブジェクトが実行中でないことを確認する必要があります。
- QObject オブジェクトのサブオブジェクトも新しいスレッドに移動する必要がある場合、これらのサブオブジェクトも moveToThread 関数を呼び出して移動する必要があります。
6. 結論
moveToThread メソッドは、必要なすべての作業をクラスにカプセル化し、各タスクをスロット関数として定義し、次にこれらのスロット関数をトリガーする信号を作成し、信号とスロットを接続して、最後に moveToThread メソッドを呼び出してこのクラスを A QThread に渡します。オブジェクトを作成し、QThread の start() 関数を呼び出して、イベント ループを完全に処理できるようにします。したがって、子スレッドに特定のタスクを実行させる必要がある場合は、対応するシグナルを送信するだけで済みます。
利点は、ワーカー クラスで実行する必要がある多くの作業を定義し、シグナルをトリガーして、子スレッドがそれを実行できることです。run() 関数内のタスクのみを実行できる QThread メソッドの継承と比較して、moveToThread メソッド内のスレッドは、対応するスロット関数が実装され、対応するシグナルがトリガーされる限り、さまざまなタスクを実行できます。