[QT プログラミング シリーズ-25]: マルチスレッド メカニズム - QThread と MoveToThread の概要

目次

第1章;序章

1.1 マルチスレッドの目的

1.2 QThread マルチスレッドの使用方法

1.3 QT は複数行のステップをサポートします

第2章 Qスレッド

2.1 概要

2.2 スレッドへの移動


第1章;序章

1.1 マルチスレッドの目的

Q Thread クラスは、プラットフォームに依存しないスレッド管理方法を提供します。

Qt でスレッドを作成する主な目的は、大量の計算、大きなファイルのコピー、ネットワーク送信など、時間のかかるバックグラウンド操作をスレッドを使用して処理することです。

QT (Qt Framework とも呼ばれます) は、クロスプラットフォーム アプリケーションを開発するための C++ ライブラリです。マルチスレッドのサポートを含む豊富な機能セットを提供します。

マルチスレッドは複数のタスクを同時に処理するための技術であり、アプリケーションの応答性とパフォーマンスを向上させることができます。

1.2 QThread マルチスレッドの使用方法

1.カスタムスレッドはQThreadクラスを継承します

2.  QObject ::moveToThread()

第2章 Qスレッド

2.1 QThread がマルチスレッドをサポートするための手順

QT でマルチスレッドを使用するには、次の手順を実行します。

  1. Qt マルチスレッド関連クラスの導入: コードに次のクラスのヘッダー ファイルを導入/インクルードします: QThreadQRunnable、 QThreadPool、QObject など。

  2. QThread または QRunnable から継承するクラスを作成します。このクラスは、バックグラウンド スレッドで実行する必要があるタスクを実行するために使用されますより柔軟な制御が必要な場合は、 QThreadから継承することを選択できます。それ以外の場合は、QRunnableから継承する方が簡単です。

  3. run() メソッドをオーバーライドする:カスタム クラスのrun() メソッドをオーバーライドし、そこにタスク ロジックを実装します。

  4. スレッドを作成して開始する: メイン スレッドでカスタム クラスのインスタンスを作成し、start() メソッドを呼び出してスレッドを開始します。これにより、 run() メソッドが自動的に呼び出されます。

以下は、QT でマルチスレッドを使用する方法を示す簡単なサンプル コードです。

#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        // 执行你的任务逻辑
        qDebug() << "Hello from thread";
    }
};

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    MyThread thread;
    thread.start();

    // 主线程继续执行其他任务
    qDebug() << "Hello from main thread";

    return app.exec();
}

上の例では、QThread を継承する MyThread というクラスを作成しました。メッセージを出力する run() メソッドをオーバーライドします。main() 関数では、MyThread インスタンスを作成し、その start() メソッドを呼び出してスレッドを開始します。その間、メインスレッドは他のタスクを実行し続けますこのプログラムを実行すると、メイン スレッドからのメッセージと子スレッドからのメッセージの 2 つのメッセージが表示されます。

複数のスレッドを使用してプログラミングする場合、競合状態やその他のスレッド関連の問題を回避するために、スレッド間の共有データ アクセスを慎重に処理する必要があることに注意してください。Qt は、スレッド間の安全なデータ アクセスの実現に役立つ、ミューテックスやセマフォなどのいくつかの同期ツールを提供します。

Qt は強力なマルチスレッド サポートを提供し、開発者がアプリケーションに同時実行性と並列処理を簡単に実装できるようにします。

以下は、Qt のマルチスレッド サポートの重要なコンポーネントと機能の一部です。

  1. QThread クラス: QThread は、Qt によって提供されるマルチスレッド プログラミングの基本クラスです。開発者は、QThread クラスを継承して独自のスレッド クラスを作成し、run() 関数を実装してスレッドの実行ロジックを定義できます。

  2. シグナルとスロットのメカニズム: Qt のシグナルとスロットのメカニズムは、強力なスレッド間通信メカニズムです。スレッド間のオブジェクト通信は、共有データを直接操作することなく、シグナルとスロットを通じて実装できますこのメカニズムにより、マルチスレッド プログラミングの複雑さを効果的に軽減できます。

  3. QMutex クラス: QMutex は、共有データへのアクセスを保護するために使用されるミューテックス クラスです。QMutex を使用すると、一度に 1 つのスレッドだけが保護されたコード領域にアクセスできるようになり、競合状態を回避できます。

  4. QWaitCondition クラス: QWaitCondition は、スレッド間の待機およびウェイク操作のための条件変数クラスです。QWaitCondition を通じて、特定の条件が満たされるまで待機するスレッドを実装し、条件が満たされたときにスレッドを起動することができます。

  5. QThreadPool クラス: QThreadPool は、スレッドの管理とスケジュールを担当するスレッド プール クラスです。QThreadPool を通じてタスクをプールに送信すると、スレッド プールがスレッドのライフ サイクルとタスクの実行を自動的に管理します。

上記のコア コンポーネントに加えて、Qt は、QReadWriteLock (読み取り/書き込みロック)、QSemaphore (セマフォ)、QtConcurrent (並列プログラミング フレームワーク) などの他の補助クラスやツールも提供します。これらのクラスとツールは、マルチスレッド プログラミングをより適切に処理するのに役立ちます。

Qt マルチスレッドを使用する場合は、次の点に注意する必要があります。

  1. 共有データへの直接アクセスを避ける:共有データを直接操作しないようにしますが、シグナルやスロットなどのメカニズム、またはミューテックスの使用を通じてスレッドの安全性を確保します。

  2. メイン スレッドを長時間ブロックしないようにする: メイン スレッドで時間のかかる操作がある場合は、メイン スレッドのブロックを回避し、アプリケーションの応答性を確保するために、その操作をバックグラウンド スレッドに配置する必要があります。

  3. スレッド間で受け渡されるデータに注意する: スレッド間でデータを受け渡すときは、ダングリング ポインターや無効な参照などの問題を避けるために、データの有効性とライフ サイクルに注意を払う必要があります。

Qt のマルチスレッド サポートにより、マルチスレッド アプリケーションをより簡単に作成できるようになり、同時実行性と並列処理において非常に便利になります。Qt のマルチスレッド機能を賢明に使用することで、アプリケーションのパフォーマンスと応答性を向上させ、マルチコア プロセッサの計算能力をより有効に活用できます。

2.2 Qスレッドの説明

QThread は、アプリケーションでスレッドを作成および管理するために Qt によって提供されるクラスです。

これにより、基礎となるスレッド操作がカプセル化され、マルチスレッド プログラミングがより便利で管理しやすくなります。

QThread を使用する一般的な手順と一般的な使用法をいくつか示します。

  1. カスタム QThread サブクラスを作成し、その run() 関数をオーバーライドします。run() 関数は、スレッドの開始時に実行されるエントリ ポイントで、ここにスレッド ロジックを記述できます。
class MyThread : public QThread {
public:
    void run() override {
        // 编写线程逻辑
    }
};
  1. MyThread クラスのインスタンスを作成し、start() 関数を呼び出してスレッドを開始します。
MyThread* thread = new MyThread();
thread->start();

  1. オプションで、スレッド間通信用の信号とスロットを接続します。Qt のシグナルとスロットのメカニズムを使用して、メインスレッドと子スレッドの間で通信できます。
connect(someObject, &SomeObject::someSignal, thread, &MyThread::someSlot);
// 或者反向连接
connect(thread, &MyThread::someSignal, someObject, &SomeObject::someSlot);

  1. 必要に応じて、wait() 関数を呼び出して、子スレッドの終了を待つことができます。quit() または terminate() 関数を呼び出してスレッドを停止することもできます。
thread->wait();      // 等待线程结束
thread->quit();      // 请求线程退出
thread->terminate(); // 强制终止线程

  1. スレッドのステータスを確認し、スレッドの動作を制御します。isRunning()、isFinished()、isInterruptionRequested() などの QThread 関数を使用して、スレッドの現在の状態を取得できます。
if (thread->isRunning()) {
    // 线程正在运行
}

QThread は、スレッド優先度の設定、スレッド スタック サイズの設定、イベント ループやイベント処理など、その他の補助機能も提供します。

QThread を直接継承して run() 関数を書き換えるのが QThread の伝統的な使用方法であることに注意してください。しかし、Qt 5.2 バージョン以降、Qt はマルチスレッドを処理するためのよりシンプルで推奨される方法を提供します。これは、Qt のシグナルおよびスロット メカニズムを QRunnable および QThreadPool または Qt Concurrent と組み合わせて使用​​することです。

この情報が QThread クラスを使用したスレッドの作成と管理に役立つことを願っています。他にご質問がございましたら、お気軽にお問い合わせください。

2.3 QThread メンバー関数

QThread クラスは、Qt でスレッドを作成および管理するために使用されるクラスです。スレッドの動作とステータスを管理するための多くのメンバー関数を提供します。

以下は、QThread クラスの一般的なメンバー関数の一部です。

  1. start(): スレッドを開始して、独立したスレッドとしてrun() 関数内のコードの実行を開始します。

  2. run():サブクラスで再実装する必要がある仮想関数は、スレッドによって実行されるメイン コード ロジックを定義します。

  3. wait(): スレッドの実行が完了するまで、現在のスレッドをブロックします。

  4. sleep(): 現在のスレッドを指定されたミリ秒間スリープ状態にします。

  5. terminate(): スレッドの実行を終了します。これは大雑把な方法であり、一般には推奨されません。

  6. quit(): スレッドのイベント ループを停止し、スレッドの実行を開始します。通常、イベント ループ (イベント ループ) で使用されます。

  7. msleep(): 現在のスレッドを指定されたミリ秒間スリープ状態にします。

  8. usleep(): 現在のスレッドを指定されたマイクロ秒数だけスリープ状態にします。

  9. isRunning(): スレッドが実行中かどうかを判断します。

  10. currentThreadId(): 現在のスレッドの一意の識別子を返します。

  11. priority() /  setPriority(): スレッドの優先度を取得または設定します。

  12. finished(): スレッドの実行が完了したときに発行されるシグナル。

  13. started(): スレッドの実行開始時に発行されるシグナル。

  14. finished(): スレッドの実行完了後に QThread オブジェクトによって送信されるシグナル。

  15. moveToThread(QThread* thread): オブジェクトを指定されたスレッドに移動して実行します。

これらは QThread クラスの一般的なメンバー関数の一部にすぎません。これらの関数を使用すると、スレッドの実行、状態、動作を管理できます。特定のアプリケーション要件に応じて、より多くの QThread メンバー関数を使用してスレッドの管理と操作を完了することができます。

第3章  moveToThread

3.1 概述

moveToThread()Qt のメンバー関数であり、オブジェクトを指定されたスレッドに移動して実行するために使用されます。

これを使用すると、マルチスレッド アプリケーション内のオブジェクトのスレッド実行コンテキストを管理できます。

moveToThread()この関数の構文と使用法は次のとおりです。

void QObject::moveToThread(QThread* thread)
  • thread: 対象スレッドへのポインタ。

moveToThread()関数の役割は、関数を呼び出すオブジェクトを指定されたスレッドに関連付けることです。

これは、オブジェクトのメソッドとイベントが、呼び出しスレッド(オブジェクトを作成したスレッド)ではなく、ターゲット スレッドのコンテキストで実行されることを意味します

moveToThread()この関数を使用した例を次に示します。

#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        qDebug() << "Worker thread:" << QThread::currentThread();
        // 执行耗时操作
    }
};

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

    qDebug() << "Main thread:" << QThread::currentThread();


    // 创建一个新的管理线程上下文的对象
    QThread* workerThread = new QThread;

    // 创建一个可以task对象,该对象可以执行某种操作,比如while循环的函数
    Worker* worker = new Worker;

    // 将Worker对象移到 workerThread 线程
    // 如果没有这行代码,则worker的task在创建worker线程中执行,而不是目标线程上下文中执行
    worker->moveToThread(workerThread);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数

    // 线程QThread也是类的对象,因此,可以通过槽函数和信号在对象间通信:
    // 在QThread对象中执行另一个需要循环执行的对象的成员函数(while循环)
    QObject::connect(workerThread, &QThread::started, worker, &Worker::doWork);

    //在QThread对象的上下文中发送一个started信号,在QThread线程对象的上下文中执行worker对象中的槽函数,该槽函数是一个while循环执行的函数。
    workerThread->start();

    return app.exec();
}

上記の例では、Workerを継承するクラスを作成しましたQObject時間のかかる作業を実行するWorkerメソッドがクラスに定義されています。doWork()

main 関数では、オブジェクトworkerworkerThreadスレッド オブジェクトを作成します。次に、 を使用してmoveToThread()オブジェクトworkerworkerThreadスレッドに移動します。このようにして、workerオブジェクトのdoWork()メソッドがworkerThreadスレッド内で実行されます。

workerThreadシグナルをスロット関数started接続すると、スレッドの開始時にメソッドが自動的に呼び出されます。workerdoWork()doWork()

moveToThread()この関数はオブジェクトが属するスレッド内でのみ呼び出すことができ、それ以外の場合は未定義の動作が発生することに注意してください。moveToThread()したがって、オブジェクトが目的のスレッドで確実に実行されるように、オブジェクトの作成後できるだけ早く関数を呼び出すことをお勧めします。

moveToThread()関数はマルチスレッド プログラミングで非常に役立ち、オブジェクトを適切なスレッドに移動し、関連するメソッドやイベント処理をそのスレッドで実行できるようになります。これにより、時間のかかる操作を UI インタラクションから分離し、アプリケーションの応答性を維持することができます。

第4章 QスレッドとmoveToThread创建线程的比较

4.1 代码实例

#include "mainwindow.h"

#include <QApplication>
#include <QThread>
#include <QDebug>

#include <QObject>
#include <QWidget>



class MyThread : public QThread {
public:
    void run() override {
        qDebug() << "Run Thread started";

        // 在这里执行耗时任务
        int i = 0;
        while(1)
        {
            sleep(3);
            qDebug() << i << ": run thread:" << QThread::currentThread();
            i++;
        }

        qDebug() << "Run Thread finished";
    }
};



class MyWorker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {

        qDebug() << "doWork Thread started";

        while(1){
            qDebug() << "Worker thread:" << QThread::currentThread();
            // 执行耗时操作
            QThread::sleep(3);
        }

        qDebug() << "doWork Thread finished";
    }
};



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    qDebug() << "Main thread started: " << QThread::currentThread() ;

    // 创建并启动run线程1
    MyThread thread_run1;
    thread_run1.start();

    // 创建并启动run线程2
    MyThread thread_run2;
    thread_run2.start();



    //创建一个管理线程上下文的对象
    QThread* workerThread1 = new QThread;

    //创建一个task对象
    MyWorker* myworker1 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    //myworker1->moveToThread(workerThread1);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread1, &QThread::started, myworker1, &MyWorker::doWork);

    workerThread1->start();


    //创建一个管理线程上下文的对象
    QThread* workerThread2 = new QThread;

    //创建一个task对象
    MyWorker* myworker2 = new MyWorker;

    // 将 Worker 对象移到 workerThread 线程
    myworker2->moveToThread(workerThread2);

    // 当线程启动时,执行 Worker 的 doWork() 槽函数
    // 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
    QObject::connect(workerThread2, &QThread::started, myworker2, &MyWorker::doWork);

    workerThread2->start();

    qDebug() << "Main thread finished" << QThread::currentThread() ;

    w.show();
    return a.exec();
}

4.2 输出结果

メインスレッドが開始されました: QThread( 0x9c7770 )

メインスレッドが終了しました QThread( 0x9c7770 )

スレッドの実行が開始されました

スレッドの実行が開始されました

doWork スレッドが開始されました

ワーカースレッド: QThread( 0x30593d0 )

doWork スレッドが開始されました

ワーカー スレッド: QThread( 0x9c7770// myworker1->moveToThread(workerThread1) がないため、メイン スレッドのコンテキストで実行されるため、実行は比較的遅く、workerThread2 のコンテキストでは myworker2 に遅れます。

0 : スレッドを実行: QThread( 0x8bfc30 )

0 : スレッドを実行: QThread( 0x8bfc20 )

ワーカースレッド: QThread(0x30593d0)

ワーカースレッド: QThread(0x9c7770)

1 : スレッドを実行: QThread(0x8bfc30)

1 : スレッドを実行: QThread(0x8bfc20)

ワーカースレッド: QThread(0x30593d0)

ワーカースレッド: QThread(0x9c7770)

おすすめ

転載: blog.csdn.net/HiWangWenBing/article/details/131715195