Qt は、スレッドをサポートするためにプラットフォームに依存しない QThread クラスを提供します。マルチスレッド プログラミングは、アプリケーションのユーザー インターフェイスをフリーズさせることなく、時間のかかる操作を実行するという問題を効果的に解決することもできます。このセクションの内容に応じて、ヘルプで「Qt のスレッド サポート」キーワードを表示できます。
ここでは、QThread の一般的な機能とスレッドを開始する 2 つの方法を紹介します。
- サブクラス化
- QObject::moveToThread 経由でワーカー オブジェクトをスレッドに移動するには、ワーカー オブジェクトを使用します。
1. QThread共通関数
よく使用される機能は、その機能に応じて次のように分類できます。
- スレッドの開始
- ボイドスタート()
- 呼び出し後、run() 関数が実行されますが、run() 関数が実行される前に start() シグナルが発行され、オペレーティング システムは優先度パラメーターに従ってスレッドをスケジュールします。スレッドがすでに実行中の場合、この関数は何も行いません。優先度パラメータの効果は、オペレーティング システムのスケジューリング ポリシーによって異なります。
- スレッドの実行
- int exec()
- 各スレッドは独自のイベント ループを持つことができ、exec() 関数を呼び出すことで開始できます。
- ボイドラン()
- スレッドの開始点。start() を呼び出した後、新しく作成されたスレッドはこの関数を呼び出します。デフォルトの実装は exec() を呼び出します。ほとんどのスレッドは、独自のスレッドを管理するためにこの関数を再実装する必要があります。関数が戻った後、アプリケーションが main() 関数を終了するのと同じように、スレッドの実行が終了します。
- スレッドが終了します
- void quit()
- スレッドにイベント ループを終了させ、成功を示す 0 を返します。これは QThread::exit(0) を呼び出すのと同じです。
- void exit(int returnCode = 0)
- スレッドがイベント ループを終了し、成功の場合は 0 を返し、失敗の場合はゼロ以外の値を返します。
- 無効終了()
- 極端な場合には、実行中のスレッドを強制的に終了したい場合は、terminate() 関数を使用できます。ただし、オペレーティング システムのスケジューリング ポリシーに応じて、スレッドがすぐに終了する場合とそうでない場合があります。問題が起こらないようにするには、terminate() の後に QThread::wait() を使用してください。
- 警告: terminate() 関数を使用すると、スレッドがいつでも終了される可能性があり、一部のクリーンアップ作業を実行できなくなります。そのため、この関数は非常に危険であり、通常は使用することはお勧めできません。絶対に必要な場合にのみ使用してください。
- void requestInterruption()
- Qt5ではスレッドを閉じるためにスレッド割り込みを要求するインターフェースを新たに導入しました。このリクエストは勧告であり、そのようなリクエストを実装するかどうか、および実装する方法はスレッド上で実行されているコードによって決定されます。この関数は、スレッド上で実行中のイベント ループを停止したり、いかなる状況でも終了したりしません。
- スレッド待機
- void msleep(符号なしロングミリ秒) [静的]
- 現在のスレッドをミリ秒ミリ秒間強制的にスリープさせます。
- void sleep(unsigned long secs) [静的]
- 現在のスレッドを強制的に secs 秒間スリープさせます。
- void usleep(unsigned long usecs) [静的]
- 現在のスレッドを強制的に usecs マイクロ秒間スリープさせます。
- bool wait(符号なし長時間 = ULONG_MAX)
- スレッドはブロックされ、ミリ秒間待機します。sleep とは異なり、wait はスレッドが終了すると戻ります。
- スレッドの状態
- bool isFinished() const
- スレッドが終了したかどうかを判断する
- bool isRunning() const
- スレッドが実行中かどうかを確認する
- bool isInterruptionRequested() const
スレッド上で実行中のタスクを停止する必要がある場合は true を返します。requestInterruption() を使用して割り込みを要求できます。Qt5 で新しく導入されたのは、長時間実行されるタスクをきれいに中断するためのインターフェイスです。関数の戻り値を決してチェックしたり操作したりしないのは安全ですが、長時間実行される関数では頻繁にチェックしたり操作したりすることをお勧めします。注: オーバーヘッドを低く抑えるために、頻繁に呼び出しすぎないでください。サンプルプログラムは以下のとおりです。 - void run() { // 処理中に終了を要求するかどうか (!isInterruptionRequested()) { // 時間のかかる操作} }
- スレッドの優先順位
- void setPriority(優先順位)
- 実行中のスレッドの優先順位を設定します。スレッドが実行されていない場合、この関数は何もせず、すぐに戻ります。start() を使用して、特定の優先順位でスレッドを開始します。priority パラメーターには、InheritPriortyd を除く QThread::Priority 列挙の任意の値を指定できます。
この記事のメリット、Qt 開発学習教材パッケージの受け取り料金、技術ビデオ、コンテンツには (C++ 言語基礎、Qt プログラミング入門、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージ図、QT ネットワーク、QT データベース プログラミング、QT プロジェクト戦闘、QSS、OpenCV、Quick モジュール、インタビューの質問など) ↓↓↓↓↓↓ 下記を参照
QThread::Priority: を列挙します。
絶え間ない |
価値 |
説明 |
QThread::IdlePriority |
0 |
他のスレッドが実行されていない場合にのみスケジュールされます |
QThread::LowestPriority |
1 |
LowPriority よりも頻度の低いスケジュール |
QThread::LowPriority |
2 |
NormalPriority よりも頻度の低いスケジューリング |
QThread::NormalPriority |
3 |
オペレーティング システムのデフォルトの優先順位 |
QThread::HighPriority |
4 |
NormalPriority よりも頻繁にスケジュールを設定する |
QThread::HighestPriority |
5 |
HighPriority よりも頻繁にスケジュールされる |
QThread::TimeCriticalPriority |
6 |
できるだけ頻繁にスケジュールを立てる |
QThread::InheritPriority |
7 |
作成されたスレッドと同じ優先順位を使用します。これがデフォルトです。 |
2. QThread をサブクラス化してスレッドを開始します
QThread は、アプリケーション内で独立して制御できるスレッドを表し、プロセス内の他のスレッドとデータを共有しますが、独立して実行されます。一般的なプログラムと比較すると、main()関数から実行が開始され、QThreadはmain()関数から実行が開始されます。デフォルトでは、run() は exec() を呼び出してイベント ループを開始します。スレッドを作成するには、QThread をサブクラス化し、run() 関数を再実装する必要があります。例えば:
class MyThread : public QThread
{
protected:
void run();
};
void MyThread::run()
{
QTcpSocket socket;
...
socket connectToHost(hostName, portNumber);
exec();
}
これにより、スレッド内に QTcpSocket が作成され、スレッドのイベント ループが実行されます。スレッドのインスタンスを外部で作成し、start() 関数を呼び出してスレッドの実行を開始できます。start() はデフォルトで run() 関数を呼び出します。run() 関数から戻ると、スレッドの実行が終了します。
スレッド内でウィジェット クラスを使用することはできないことに注意してください。
サンプルプログラム
グラフィカル インターフェイス プログラムでスレッドを開始する例を見てみましょう。インターフェイスには 2 つのボタンがあり、1 つはスレッドの開始に使用され、もう 1 つはスレッドの終了に使用されます。myThread という名前、Dialog というクラス名で新しい Qt Gui アプリケーションを作成し、基本クラスとして QDialog を選択します。完了後、デザイン モードに入り、インターフェイスに 2 つのプッシュ ボタン ボタンを配置し、最初のボタンの表示テキストを「スレッドの開始」に変更し、名前を startButton に変更します。2 番目のボタンの表示テキストを「スレッドの終了」に変更し、名前を stopButton に変更し、有効な属性のチェックを外します。次に、新しい C++ クラスをプロジェクトに追加し、クラス名を「MyThread」に、基本クラスを「QThread」に設定し、型情報として「QObject から継承」を選択します。完了したら、mythread.h ファイルを入力し、次のように変更します。
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void stop();
protected:
void run();
private:
volatile bool stopped;
};
#endif // MYTHREAD_H
ここで、stopped 変数は volatile キーワードを使用しており、変数を常に最新の状態に保つため、複数のスレッドでアクセスする際のエラーが回避されます。次に、mythread.cpp ファイルを入力し、次のように変更します。
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
stopped = false;
}
void MyThread::run()
{
qreal i = 0;
while (!stopped)
{
qDebug() << QString("in MyThread: %1").arg(i);
msleep(1000);
i++;
}
stopped = false;
}
void MyThread::stop()
{
stopped = true;
}
(1) 停止した変数はコンストラクタ内で false に初期化されます。
(2) run()関数内で停止した変数の値を常に判定し、falseである限りiの値を1秒ごとにインクリメントした文字列を出力します。
(3) top() 関数で stop 変数を true に設定すると、run() 関数のループが終了し、run() 関数から抜けてスレッド全体が終了します。ここでは、stopped 変数を使用してプロセスを終了します。危険な terminate() 関数は使用されず、デストラクタでは quit()、wait()、および requestInterruption() 関数も使用されません。
以下では、Dialog クラスのカスタム スレッドを使用します。まず、dialog.h ファイルに移動し、ヘッダー ファイル「mythread.h」を追加してから、プライベート オブジェクトを追加します。
#include "mythread.h"
...
private:
MyThread thread;
次にデザインモードに移行し、2つのボタンのクリック信号に対応するスロットをそれぞれ入力し、次のように変更します。
// 启动线程按钮
void Dialog::on_startButton_clicked()
{
thread.start();
ui->startButton->setEnabled(false);
ui->stopButton->setEnabled(true);
}
// 终止线程按钮
void Dialog::on_stopButton_clicked()
{
if (thread.isRunning())
{
thread.stop();
ui->startButton->setEnabled(true);
ui->stopButton->setEnabled(false);
}
}
start() 関数はスレッドの開始時に呼び出され、2 つのボタンの状態が設定されます。スレッドを終了するときは、まず isRunning() を使用してスレッドが実行中かどうかを判断し、実行中の場合は stop() 関数を呼び出してスレッドを終了し、2 つのボタンの状態を変更します。ここでプログラムを実行し、「スレッドの開始」ボタンをクリックしてアプリケーション出力列の出力を表示し、「スレッドの終了」ボタンを押すと、出力が停止したことがわかります。
3、ワーカーオブジェクトでスレッドを開始する方法
スレッドを作成するもう 1 つの方法は、ワーカー オブジェクトを使用して QObject::moveToThread 経由でスレッドに移動することです。例えば:
//Worker类:在线程中执行的类,例如定时器超时操作
class Worker类 : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter)
{
QString result;
// 这里是阻塞的操作
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
//Controller类:线程所在的类
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller()
{
Worker *worker = new Worker;
//将worker对象的线程从主线程移动到workerThread
worker->moveToThread(&workerThread);
//当workerThread线程结束后,会自动销毁该线程
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
//当Controller类发射了operate()信号,会调用worker对象的doWork槽函数
connect(this, &Controller::operate, worker, &Worker::doWork);
//当workerr类发射了resultReady()信号,会调用Controller对象的handleResults槽函数
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
//最开始,启动workerThread线程
workerThread.start();
}
~Controller()
{
//在析构函数中终止进程
workerThread.requestInterruption();
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
このようにして、ワーカー スロットのコードが別のスレッドで実行されるため、時間のかかる一部の操作を別のワーカー スレッドで簡単に完了できます。任意のスレッドの任意のオブジェクトの任意のシグナルをワーカーのスロットに関連付けることができるため、異なるスレッド間でシグナルとスロットを関連付けても安全です。
注: さらに、時間があれば、ブログ「Qt-Threads and Timers」を参照して、スレッドを開始する 2 つの方法について詳しく学ぶことができます。
4番目、スレッドを閉じます
スレッドを閉じるには多くの方法がありますが、ここでは最も一般的な方法の 1 つを示します。デストラクターで quit()、wait()、および requestInterruption() 関数を使用します。サンプルプログラムは以下のとおりです。
~WorkerThread()
{
// 请求终止
requestInterruption();
quit();
wait();
}
記事はブログガーデン(fengMisaka)から転載しました:Qtプロセスとスレッドその2:スレッドを開始する2つの方法
この記事のメリット、Qt 開発学習教材パッケージの受け取り料金、技術ビデオ、コンテンツには (C++ 言語基礎、Qt プログラミング入門、QT シグナルとスロットのメカニズム、QT インターフェイス開発イメージ図、QT ネットワーク、QT データベース プログラミング、QT プロジェクト戦闘、QSS、OpenCV、Quick モジュール、インタビューの質問など) ↓↓↓↓↓↓ 下記を参照