「Qtの基礎 - マルチスレッド」の版間の差分
349行目: | 349行目: | ||
== QMutexクラス == | == QMutexクラス == | ||
==== QMutextクラスとは ==== | |||
QMutexクラスは、スレッド間のアクセスシリアル化を提供する。<br> | QMutexクラスは、スレッド間のアクセスシリアル化を提供する。<br> | ||
QMutexの目的は、オブジェクト、データ構造、ソースコードのセクションを保護して、1度に1つのスレッドのみがアクセスできるようにする。(同期)<br> | QMutexの目的は、オブジェクト、データ構造、ソースコードのセクションを保護して、1度に1つのスレッドのみがアクセスできるようにする。(同期)<br> | ||
376行目: | 377行目: | ||
</center> | </center> | ||
<br> | <br> | ||
==== 変数の排他制御 ==== | |||
以下の例では、スレッドの動作を制御するためのメンバ変数bStopを定義している。<br> | 以下の例では、スレッドの動作を制御するためのメンバ変数bStopを定義している。<br> | ||
変数bStopがtrueの場合、スレッドはループから抜ける。<br> | 変数bStopがtrueの場合、スレッドはループから抜ける。<br> | ||
482行目: | 484行目: | ||
"B" 4 | "B" 4 | ||
"B" 5 | "B" 5 | ||
<br> | |||
==== ファイルの排他制御 ==== | |||
以下の例では、QMutexクラスを使用してファイルアクセスを同期化して、各関数でテキストファイルに排他的にアクセスして読み書きしている。<br> | |||
<br> | |||
QMutexクラスのインスタンスをグローバルに定義して、ファイルアクセスの排他制御に使用する。<br> | |||
<br> | |||
QMutexLockerクラスを使用することにより、ロックの取得と解放が自動的に行われて、例外が発生した場合でも確実にロックが解放される。<br> | |||
各関数は排他的にファイルにアクセスするため、データの競合や不整合を防ぐことができる。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>タスクの実行順序は保証されないため、ファイルの最終的な内容は実行ごとに異なる可能性がある。</u><br> | |||
<u>ファイル操作のエラー処理をより堅牢にする必要がある。</u><br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QCoreApplication> | |||
#include <QtConcurrent> | |||
#include <QFuture> | |||
#include <QThread> | |||
#include <QMutex> | |||
#include <QFile> | |||
#include <QTextStream> | |||
#include <QDebug> | |||
QMutex fileMutex; | |||
int readFromFile(QString &content) | |||
{ | |||
QFile file("sample.txt"); | |||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | |||
qDebug() << "Error: Unable to open file for reading"; | |||
return -1; | |||
} | |||
QTextStream in(&file); | |||
QString content = in.readAll(); | |||
file.close(); | |||
return 0; | |||
} | |||
int writeToFile(const QString &content) | |||
{ | |||
QFile file("sample.txt"); | |||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) { | |||
qDebug() << "Error: Unable to open file for writing"; | |||
return -1; | |||
} | |||
QTextStream out(&file); | |||
out << content; | |||
file.close(); | |||
return 0; | |||
} | |||
int task1() | |||
{ | |||
QMutexLocker locker(&fileMutex); // ロックを取得 | |||
qDebug() << "Task 1 started in thread: " << QThread::currentThreadId(); | |||
// ファイルを読み込み表示する | |||
QString content = ""; | |||
readFromFile(content); | |||
qDebug() << "Task 1 read: " << content; | |||
// ファイルへ書き込む | |||
writeToFile("Task 1 was here\n"); | |||
QThread::sleep(2); // 2秒待機 | |||
qDebug() << "Task 1 finished"; | |||
return 0; // ロッカーのデストラクタが呼ばれて、ロックが解放される | |||
} | |||
int task2() | |||
{ | |||
QMutexLocker locker(&fileMutex); // ロックを取得 | |||
qDebug() << "Task 2 started in thread: " << QThread::currentThreadId(); | |||
// ファイルを読み込み表示する | |||
QString content = ""; | |||
readFromFile(content); | |||
qDebug() << "Task 2 read: " << content; | |||
writeToFile("Task 2 was here\n"); | |||
QThread::sleep(5); // 5秒待機 | |||
qDebug() << "Task 2 finished"; | |||
return 0; // ロッカーのデストラクタが呼ばれて、ロックが解放される | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
qDebug() << "Main thread: " << QThread::currentThreadId(); | |||
// ファイル内容を設定 | |||
writeToFile("Initial content\n"); | |||
// 2つのタスクを非同期で実行 | |||
QFuture<int> future1 = QtConcurrent::run(task1); | |||
QFuture<int> future2 = QtConcurrent::run(task2); | |||
// 両方のタスクが完了するまで待機し、結果を取得 | |||
int result1 = future1.result(); | |||
int result2 = future2.result(); | |||
qDebug() << "Task 1 result:" << result1; | |||
qDebug() << "Task 2 result:" << result2; | |||
qDebug() << "All tasks completed"; | |||
// 最終的なファイル内容を表示 | |||
qDebug() << "Final file content:"; | |||
qDebug() << readFromFile(); | |||
return a.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br><br> | <br><br> | ||
2024年8月13日 (火) 03:14時点における版
概要
各プログラムは起動時に1つのスレッドを持っている。
Qtでは、このスレッドのことをメインスレッドまたはGUIスレッドと呼ぶ。
Qt GUIでは、このスレッドで実行する必要がある。
全てのウィジェットやQPixmap等のいくつかのクラスは、セカンダリスレッドでは動作しない。
一般的に、セカンダリスレッドのことをワーカースレッドと呼ぶが、これはメインスレッドから処理をオフロードするために使用される。
データへの同時アクセス
2つのスレッドが同じオブジェクトへのポインタを持つ場合、
両方のスレッドが同時にそのオブジェクトにアクセスする可能性があるため、オブジェクトが破壊される可能性がある。
1つのオブジェクトに対する複数のスレッドによる同時操作を防止しなければならない。
マルチスレッドは、以下のような2つの使用例がある。
- マルチコアプロセッサを利用して処理を高速化する。
- 長い処理をオフロードまたは他のスレッドへの呼び出しをブロックすることで、GUIスレッドやその他の時間的に重要なスレッドの応答性を維持する。
マルチスレッドの代替方法
マルチスレッドを作成する前に、考えられる代替案を検討する必要がある。
下表は、Threading Basicから引用したものである。
表. マルチスレッドの代替方法
代替案 | 説明 |
---|---|
QEventLoop::processEvents() | 時間が掛かる処理内において、QEventLoop::processEvents() を繰り返し呼び出すことで、GUIのブロッキングを防ぐことができる。ただし、ハードウェアによっては、 processEvents() の呼び出しが頻繁に発生しすぎたり、十分な頻度で発生しないことがあるため、このソリューションはうまく拡張できない。 |
QSocketNotifierクラス QNetworkAccessManagerクラス QIODevice::readyRead() |
1つまたは複数のスレッドを持ち、それぞれが低速なネットワーク接続上でブロッキングリードを行う代替手段である。 ネットワークデータに応答する計算を素早く実行できる限り、このリアクティブ設計は、スレッドで同期的に待機するよりも優れている。 リアクティブ設計は、スレッド処理よりもエラーが発生しにくく、エネルギー効率が高い。 多くの場合、パフォーマンス面でもメリットがある。 |
QTimerクラス | バックグラウンド処理は、将来のある時点でスロットの実行をスケジュールするために、タイマを使用して便利に行うことができる。 インターバルが0のタイマは、処理すべきイベントがなくなるとすぐにタイムアウトする。 |
マルチスレッドの設計方法
Qtにおいて、推奨されるマルチスレッドの設計方法を示す。
下表は、Threading Basicから引用したものである。
表. 推奨されるマルチスレッドの設計方法
スレッドの寿命 | 設計方法 | 解決策 |
---|---|---|
1度のみ | 別のスレッド内でメソッドを実行して、 メソッドが終了した時にスレッドを終了する。 |
Qtでは異なる解決策が用意されている。
|
1度のみ | 長時間実行している操作は、別のスレッドに入れる必要がある。 処理の途中で、GUIスレッドにステータス情報を送る必要がある。 |
まず、QThreadクラスを継承したクラスを作成して、runメソッドをオーバーライドする。 |
1度のみ | 操作は、コンテナの全ての項目に対して行う。 処理は、利用可能な全てのコアを使用して実行する必要がある。 例: 画像のリストからサムネイルを生成する等。 |
QtConcurrentクラスでは、コンテナの要素ごとに操作を適用するmapメソッド、 |
永久 | オブジェクトを別のスレッドに常駐させて、 要求に応じて異なるタスクを実行する。 つまり、ワーカースレッドとの通信が必要になる。 |
QObjectクラスを継承したクラスを作成して、必要なスロットとシグナルを実装する。 |
永久 | オブジェクトを別のスレッドに常駐させて、ポートのポーリングや、 GUIスレッドと通信できるタスク等を繰り返し実行する。 |
上記と同様であるが、ワーカースレッドにてタイマを使用してポーリングを実装する。 |
スレッドの作成 : QThreadクラスの使用 (非推奨)
以下に示す方法は、スレッドに低機能APIを使用しているだけでなく、スケーリングの問題が発生する可能性があるため非推奨としている。
Qtにおいて、マルスレッドを設計する場合は、高機能APIを備えたQtConcurrent
を使用することを推奨する。
QtConcurrent
は、スレッドの実行をキャンセル、一時停止、再開することができる。
ただし、低機能APIについても知る必要がある。
以下の例では、QThread
クラスを継承して、派生クラスを作成している。
3つのスレッドが同時に実行されて、同じコードセグメントにアクセスしている。
// main.cpp
#include <QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread1("A"), thread2("B"), thread3("C");
thread1.start();
thread2.start();
thread3.start();
return a.exec();
}
QThread
クラスのstart
メソッドは、派生クラスでオーバーライドしたrun
メソッドを呼び出すことにより、スレッドの実行を開始する。
// mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QString>
class MyThread : public QThread
{
public:
// constructor
// set name using initializer
explicit MyThread(QString s);
// overriding the QThread's run() method
void run();
private:
QString name;
};
#endif // MYTHREAD_H
// mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QString s) : name(s)
{
}
// We overrides the QThread's run() method here
// run() will be called when a thread starts
// the code will be shared by all threads
void MyThread::run()
{
for(int i = 0; i <= 100; i++)
{
qDebug() << this->name << " " << i;
}
}
スレッドの作成 : QtConcurrentクラスの使用 (推奨)
QtConcurrent
とは、ミューテックス、読み込み / 書き込みロック、待機条件、セマフォ等の低機能のスレッドプリミティブを使用せずに、
マルチスレッドを作成できる高機能APIである。
QtConcurrent
を使用して作成したスレッドは、使用可能なプロセッサコアの数に応じて、使用されるスレッドの数を自動的に調整する。
つまり、現在作成されているアプリケーションは、将来マルチコアシステムに導入された場合でも拡張を続ける。
以下の例では、上記のセクションと同じ出力を取得している。
設計者がスレッドを作成する必要はなく、適切なパラメータを使用して関数を定義するだけである。
QtConcurrent
のAPIは、以下の通りである。
QtConcurrent::run()
- 別のスレッドで関数を実行する。
QFuture
クラス- 非同期処理の結果を表す。
QFuture
クラスは、cancel()
、pause()
、resume()
等の実行中の計算と対話する方法も提供している。- ただし、以下の例では、
QtConcurrent::run()
がQFuture
クラスを返すため、これを行うことはできない。 - これについては、
QtConcurrent::mappedReduced()
の後半のセクションを参照すること。
# .proファイル
QT += concurrent
# CMakeLists.txtファイル
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Concurrent)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Concurrent)
target_link_libraries(<ターゲット名> PRIVATE
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::Concurrent
)
- 戻り値がある場合
#include <QCoreApplication>
#include <QtConcurrent>
#include <QFuture>
#include <QThread>
#include <QDebug>
int task1()
{
qDebug() << "Task 1 started in thread:" << QThread::currentThreadId();
QThread::sleep(5); // 5秒待機
qDebug() << "Task 1 finished";
return 10;
}
QString task2()
{
qDebug() << "Task 2 started in thread:" << QThread::currentThreadId();
QThread::sleep(10); // 10秒待機
qDebug() << "Task 2 finished";
return "Task 2 finished";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread: " << QThread::currentThreadId();
// 2つのタスクを非同期で実行
QFuture<int> future1 = QtConcurrent::run(task1);
QFuture<QString> future2 = QtConcurrent::run(task2);
// 両方のタスクが完了するまで待機して、結果を取得
// QFuture::resultメソッドはタスクが完了するまでブロックするため、waitForFinishedメソッドを明示的に呼び出す必要がない
int result1 = future1.result();
QString result2 = future2.result();
qDebug() << "Task 1 result: " << result1;
qDebug() << "Task 2 result: " << result2;
qDebug() << "All tasks completed";
return a.exec();
}
- 戻り値が無い場合
#include <QCoreApplication>
#include <QtConcurrent>
#include <QFuture>
#include <QThread>
void func(QString name)
{
for(int i = 0; i <= 5; i++)
{
qDebug() << name << " " << i << "from" << QThread::currentThread();
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// QtConcurrent::runメソッドを使用して、func関数を別々のスレッドで非同期に実行
// また、QFutureクラスのオブジェクトを使用して、各タスクの実行状況を追跡
QFuture<void> t1 = QtConcurrent::run(func, QString("A"));
QFuture<void> t2 = QtConcurrent::run(func, QString("B"));
QFuture<void> t3 = QtConcurrent::run(func, QString("C"));
// waitForFinishedメソッドを使用して、両方のタスクが完了するまで待機
t1.waitForFinished();
t2.waitForFinished();
t3.waitForFinished();
return a.exec();
}
スレッドのプライオリティ
優先度に応じて、スレッドがどのように動作するかを記載する。
一般的に、スレッドの優先順位を設定する部分を除いて、"スレッドの作成(非推奨)"セクションと同じである。
優先度は、QThread
クラスのstart
メソッドに渡されるパラメータにより設定される。
その定義は、以下のようになる。
void QThread::start(Priority priority = InheritPriority)
start
メソッドは、run
メソッドを呼び出してスレッドの実行を開始する。(既にスレッドが実行されている場合、start
メソッドはNOP)
OSは、優先度パラメータにしたがってスレッドをスケジュールする。
優先度パラメータの効果は、OSのスケジューリングポリシーによって異なる。
特に、スレッドの優先順位をサポートしていないシステムでは、優先順位は無視される。
表. enum QThread::Priority
定数 | 値 | 説明 |
---|---|---|
QThread::IdlePriority | 0 | 他のスレッドが実行されていない場合にのみ、スケジュールされる。 |
QThread::LowestPriority | 1 | LowPriorityよりも少ない頻度でスケジュールされる。 |
QThread::NormalPriority | 3 | OSの標準の優先度。 |
QThread::HighestPriority | 5 | HighPriorityよりも頻繁にスケジュールされる。 |
QThread::QThread::InheritPriority | 7 | 作成したスレッドと同じ優先度を使用する。(初期状態) |
// main.cppファイル
#include <QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread thread1("A"), thread2("B"), thread3("C");
thread1.start(QThread::LowestPriority);
thread2.start();
thread3.start(QThread::HighestPriority);
return a.exec();
}
上記のサンプルコードの実行結果から、SUSEのスケジューラは、スレッドの優先順位を尊重しているようである。
プライオリティの低い"A"スレッドが最後に終了している。
...略 "A" 96 "A" 97 "A" 98 "A" 99 "A" 100
優先順位の逆転の問題がある場合、 優先順位の逆転を参照すること。
QMutexクラス
QMutextクラスとは
QMutexクラスは、スレッド間のアクセスシリアル化を提供する。
QMutexの目的は、オブジェクト、データ構造、ソースコードのセクションを保護して、1度に1つのスレッドのみがアクセスできるようにする。(同期)
一般的に、QMutexLockerクラスを使用することが最適である。
これにより、ロックとロック解除が一貫して実行されるようになる。
QMutex::QMutex(RecursionMode mode = NonRecursive)
- QMutexクラスのインスタンスを生成して、新しいミューテックスを構築する。
ミューテックスは、ロック解除された状態で生成される。 - ミューテックスのモードがQMutex::Recursiveの場合、スレッドは同じミューテックスを複数回ロックでき、
対応する数のunlockメソッドの呼び出しが行われるまで、ミューテックスのロックは解除されない。 - それ以外の場合、スレッドはミューテックスを1回だけロックできる。
モードを指定しない場合は、QMutex::NonRecursiveが指定される。
表. enum QMutex::RecursionMode
定数 | 値 | 説明 |
---|---|---|
QMutex::Recursive | 1 | スレッドは、同じミューテックスを複数回ロックでき、 対応する数のunlockメソッドの呼び出しが行われるまで、ミューテックスはロック解除されない。 |
QMutex::NonRecursive | 0 | スレッドは、ミューテックスを1回だけロックできる。 |
変数の排他制御
以下の例では、スレッドの動作を制御するためのメンバ変数bStopを定義している。
変数bStopがtrueの場合、スレッドはループから抜ける。
したがって、1つのスレッドからのみアクセスする必要があり、ミューテックスのロックを使用している。
// main.cppファイル
#include <QCoreApplication>
#include <QDebug>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// creating three thread instances
MyThread thread1("A"), thread2("B"), thread3("C");
qDebug() << "hello from GUI thread " << a.thread()->currentThreadId();
// thread start -> call run()
thread1.start();
thread2.start();
thread3.start();
return a.exec();
}
// mythread.hファイル
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QString>
class MyThread : public QThread
{
public:
// constructor
// set name and Stop is set as false by default
MyThread(QString s, bool b = false);
// overriding the QThread's run() method
void run();
// variable that mutex protects
bool bStop;
private:
QString name;
};
#endif // MYTHREAD_H
// mythread.cppファイル
#include "mythread.h"
#include <QDebug>
#include <QMutex>
MyThread::MyThread(QString s, bool b) : name(s), bStop(b)
{
}
// run() will be called when a thread starts
void MyThread::run()
{
qDebug() << this->name << " " << this->Stop;
for(int i = 0; i <= 5; i++)
{
QMutex mutex;
// prevent other threads from changing the "Stop" value
mutex.lock();
if(this->bStop) break;
mutex.unlock();
qDebug() << this->name << " " << i;
}
}
# 出力 hello from GUI thread 0x1364 "A" false "C" false "C" 0 "A" 0 "A" 1 "A" 2 "A" 3 "A" 4 "C" 1 "A" 5 "B" false "C" 2 "C" 3 "C" 4 "C" 5 "B" 0 "B" 1 "B" 2 "B" 3 "B" 4 "B" 5
ファイルの排他制御
以下の例では、QMutexクラスを使用してファイルアクセスを同期化して、各関数でテキストファイルに排他的にアクセスして読み書きしている。
QMutexクラスのインスタンスをグローバルに定義して、ファイルアクセスの排他制御に使用する。
QMutexLockerクラスを使用することにより、ロックの取得と解放が自動的に行われて、例外が発生した場合でも確実にロックが解放される。
各関数は排他的にファイルにアクセスするため、データの競合や不整合を防ぐことができる。
※注意
タスクの実行順序は保証されないため、ファイルの最終的な内容は実行ごとに異なる可能性がある。
ファイル操作のエラー処理をより堅牢にする必要がある。
#include <QCoreApplication>
#include <QtConcurrent>
#include <QFuture>
#include <QThread>
#include <QMutex>
#include <QFile>
#include <QTextStream>
#include <QDebug>
QMutex fileMutex;
int readFromFile(QString &content)
{
QFile file("sample.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Error: Unable to open file for reading";
return -1;
}
QTextStream in(&file);
QString content = in.readAll();
file.close();
return 0;
}
int writeToFile(const QString &content)
{
QFile file("sample.txt");
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
qDebug() << "Error: Unable to open file for writing";
return -1;
}
QTextStream out(&file);
out << content;
file.close();
return 0;
}
int task1()
{
QMutexLocker locker(&fileMutex); // ロックを取得
qDebug() << "Task 1 started in thread: " << QThread::currentThreadId();
// ファイルを読み込み表示する
QString content = "";
readFromFile(content);
qDebug() << "Task 1 read: " << content;
// ファイルへ書き込む
writeToFile("Task 1 was here\n");
QThread::sleep(2); // 2秒待機
qDebug() << "Task 1 finished";
return 0; // ロッカーのデストラクタが呼ばれて、ロックが解放される
}
int task2()
{
QMutexLocker locker(&fileMutex); // ロックを取得
qDebug() << "Task 2 started in thread: " << QThread::currentThreadId();
// ファイルを読み込み表示する
QString content = "";
readFromFile(content);
qDebug() << "Task 2 read: " << content;
writeToFile("Task 2 was here\n");
QThread::sleep(5); // 5秒待機
qDebug() << "Task 2 finished";
return 0; // ロッカーのデストラクタが呼ばれて、ロックが解放される
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread: " << QThread::currentThreadId();
// ファイル内容を設定
writeToFile("Initial content\n");
// 2つのタスクを非同期で実行
QFuture<int> future1 = QtConcurrent::run(task1);
QFuture<int> future2 = QtConcurrent::run(task2);
// 両方のタスクが完了するまで待機し、結果を取得
int result1 = future1.result();
int result2 = future2.result();
qDebug() << "Task 1 result:" << result1;
qDebug() << "Task 2 result:" << result2;
qDebug() << "All tasks completed";
// 最終的なファイル内容を表示
qDebug() << "Final file content:";
qDebug() << readFromFile();
return a.exec();
}
スレッドの作成 : GUI
QThreadクラスの使用
ここでは、ダイアログとボタンコントロールを使用したマルチスレッドの作成方法を記載する。
以下の例では、ダイアログの[開始]ボタンを押下することでスロットがトリガーされて、スレッドのstartメソッドを呼び出す。
startメソッドは、valueChangedシグナルが発行されるスレッドのrunメソッドを呼び出す。
valueChangedシグナルはonValueChangedスロットに接続されて、ダイアログのカウントラベルを更新する。
// main.cppファイル
#include <QApplication>
#include "dialog.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
// dialog.hファイル
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QString>
#include "mythread.h"
namespace Ui {class Dialog;}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
MyThread *mThread;
private:
Ui::Dialog *ui;
public slots:
void onValueChanged(int);
private slots:
// for Start button
void on_pushButton_clicked();
// for Stop button
void on_pushButton_2_clicked();
};
#endif // DIALOG_H
// dialog.cppファイル
#include "dialog.h"
#include "ui_dialog.h"
#include "mythread.h"
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
// create an instance of MyThread
mThread = new MyThread(this);
// connect signal/slot
connect(mThread, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int)));
}
Dialog::~Dialog()
{
delete ui;
}
// Absorb the signal emitted from a run() method and reflect the count change to the count label in our dialog
void Dialog::onValueChanged(int count)
{
ui->label->setText(QString::number(count));
}
// Start button
void Dialog::on_pushButton_clicked()
{
mThread->start();
}
// Stop button
void Dialog::on_pushButton_2_clicked()
{
mThread->Stop = true;
}
// mythread.hファイル
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0, bool b = false);
void run();
// if Stop = true, the thread will break
// out of the loop, and will be disposed
bool Stop;
signals:
// To communicate with Gui Thread
// we need to emit a signal
void valueChanged(int);
};
#endif // MYTHREAD_H
// mythread.cppファイル
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent, bool b) : QThread(parent), Stop(b)
{
}
// run() will be called when a thread starts
void MyThread::run()
{
for(int i = 0; i <= 100; i++)
{
QMutex mutex;
// prevent other threads from changing the "Stop" value
mutex.lock();
if(this->Stop) break;
mutex.unlock();
// emit the signal for the count label
emit valueChanged(i);
// slowdown the count change, msec
this->msleep(500);
}
}
QtConcurrentクラスの使用
以下の例では、QtConcurrent
クラスとQEventLoop
クラスを使用したマルチスレッドである。
まず、.proファイルのQTにconcurrentを追記する。
# .proファイル QT += 〜 concurrent
#include <QtConcurrent>
void MainWindow::onBtnClicked()
{
//-> マルチスレッド開始
QEventLoop eventLoop;
QtConcurrent::run([this, &eventLoop]()
{
heavyFunction(); // 時間の掛かる処理
eventLoop.quit();
});
eventLoop.exec();
//<- マルチスレッド終了
return;
}
音声ファイルの再生が終了するまで待機
以下の例では、音声ファイルの再生が終了するまで待機している。
// MainWindow.cpp
#include <qtconcurrentrun.h>
#include <QtMultimedia>
// ...略
void MainWindow::PlaySound()
{
QFuture<void> Task = QtConcurrent::run([]()
{
QSoundEffect effect;
QEventLoop loop;
QString strFilePath = QCoreApplication::applicationDirPath() + QDir::separator() + tr("Sample.wav");
effect.setSource(QUrl::fromLocalFile(strFilePath));
effect.setVolume(50);
effect.play();
QObject::connect(&effect, &QSoundEffect::playingChanged, [&loop](){loop.exit();});
loop.exec();
});
Task.waitForFinished();
}
// ...略
# 〜.pro
QT += core gui multimedia concurrent
# ...略