MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - マルチスレッドのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - マルチスレッド
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == 各プログラムは起動時に1つのスレッドを持っている。<br> Qtでは、このスレッドのことをメインスレッドまたはGUIスレッドと呼ぶ。<br> Qt GUIでは、このスレッドで実行する必要がある。<br> <br> 全てのウィジェットやQPixmap等のいくつかのクラスは、セカンダリスレッドでは動作しない。<br> 一般的に、セカンダリスレッドのことをワーカースレッドと呼ぶが、これはメインスレッドから処理をオフロードするために使用される。<br> <br> データへの同時アクセス<br> 2つのスレッドが同じオブジェクトへのポインタを持つ場合、<br> 両方のスレッドが同時にそのオブジェクトにアクセスする可能性があるため、オブジェクトが破壊される可能性がある。<br> 1つのオブジェクトに対する複数のスレッドによる同時操作を防止しなければならない。<br> <br> マルチスレッドは、以下のような2つの使用例がある。<br> * マルチコアプロセッサを利用して処理を高速化する。 * 長い処理をオフロードまたは他のスレッドへの呼び出しをブロックすることで、GUIスレッドやその他の時間的に重要なスレッドの応答性を維持する。 <br><br> == マルチスレッドの代替方法 == マルチスレッドを作成する前に、考えられる代替案を検討する必要がある。<br> 下表は、[http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html Threading Basic]から引用したものである。<br> <center> 表. マルチスレッドの代替方法<br> {| class="wikitable" style="background-color:#fefefe;" |- ! style="background-color:#00ffff;" | 代替案 ! style="background-color:#00ffff;" | 説明 |- | QEventLoop::processEvents() || 時間が掛かる処理内において、<code>QEventLoop::processEvents()</code>を繰り返し呼び出すことで、GUIのブロッキングを防ぐことができる。<br>ただし、ハードウェアによっては、<code>processEvents()</code>の呼び出しが頻繁に発生しすぎたり、十分な頻度で発生しないことがあるため、<br>このソリューションはうまく拡張できない。 |- | QSocketNotifierクラス<br>QNetworkAccessManagerクラス<br>QIODevice::readyRead() || 1つまたは複数のスレッドを持ち、それぞれが低速なネットワーク接続上でブロッキングリードを行う代替手段である。<br>ネットワークデータに応答する計算を素早く実行できる限り、このリアクティブ設計は、スレッドで同期的に待機するよりも優れている。<br>リアクティブ設計は、スレッド処理よりもエラーが発生しにくく、エネルギー効率が高い。<br>多くの場合、パフォーマンス面でもメリットがある。 |- | QTimerクラス || バックグラウンド処理は、将来のある時点でスロットの実行をスケジュールするために、タイマを使用して便利に行うことができる。<br>インターバルが0のタイマは、処理すべきイベントがなくなるとすぐにタイムアウトする。 |} </center> <br><br> == マルチスレッドの設計方法 == Qtにおいて、推奨されるマルチスレッドの設計方法を示す。<br> 下表は、[http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html Threading Basic]から引用したものである。<br> <center> 表. 推奨されるマルチスレッドの設計方法<br> {| class="wikitable" style="background-color:#fefefe;" |- ! style="background-color:#00ffff;" | スレッドの寿命 ! style="background-color:#00ffff;" | 設計方法 ! style="background-color:#00ffff;" | 解決策 |- | 1度のみ || 別のスレッド内でメソッドを実行して、<br>メソッドが終了した時にスレッドを終了する。 || Qtでは異なる解決策が用意されている。<br> # メソッド内において、QtConcurrent::run()を実行する。<br> # QRunnableクラスを継承したクラスを作成して、<br>QThreadPool::globalInstance()->start()を実行する。<br>(グローバルスレッドプール内)<br> # QThreadクラスを継承したクラスを作成して、runメソッドをオーバーライドする。<br>次に、QThread::start()を実行する。 |- | 1度のみ || 長時間実行している操作は、別のスレッドに入れる必要がある。<br>処理の途中で、GUIスレッドにステータス情報を送る必要がある。 || まず、QThreadクラスを継承したクラスを作成して、runメソッドをオーバーライドする。<br>オーバーライドしたrunメソッド内で、シグナルを送信する。<br> 次に、そのシグナルをGUIスレッドのスロットに接続する。 |- | 1度のみ || 操作は、コンテナの全ての項目に対して行う。<br>処理は、利用可能な全てのコアを使用して実行する必要がある。<br>例: 画像のリストからサムネイルを生成する等。 || QtConcurrentクラスでは、コンテナの要素ごとに操作を適用するmapメソッド、<br> コンテナの要素を選択するfilterメソッド、残りの要素を結合するreduceメソッド等、<br> 指定するオプションが用意されている。 |- | 永久 || オブジェクトを別のスレッドに常駐させて、<br>要求に応じて異なるタスクを実行する。<br>つまり、ワーカースレッドとの通信が必要になる。 || QObjectクラスを継承したクラスを作成して、必要なスロットとシグナルを実装する。<br> オブジェクトを実行中のイベントループを持つスレッドに移動して、<br> シグナル / スロット接続を介してオブジェクトと通信する。 |- | 永久 || オブジェクトを別のスレッドに常駐させて、ポートのポーリングや、<br>GUIスレッドと通信できるタスク等を繰り返し実行する。 || 上記と同様であるが、ワーカースレッドにてタイマを使用してポーリングを実装する。<br> ただし、ポーリングによる解決策は止めた方がよい。<br> QSocketNotifierクラスを使用することもある。 |} </center> <br><br> == スレッドの作成(非推奨) == 以下に示す方法は、スレッドに低機能APIを使用しているだけでなく、スケーリングの問題が発生する可能性があるため非推奨としている。<br> <br> Qtにおいて、マルスレッドを設計する場合は、高機能APIを備えた<code>QtConcurrent</code>を使用することを推奨する。<br> <code>QtConcurrent</code>は、スレッドの実行をキャンセル、一時停止、再開することができる。<br> <br> ただし、低機能APIについても知る必要がある。<br> <br> 以下の例では、<code>QThread</code>クラスを継承して、派生クラスを作成している。<br> 3つのスレッドが同時に実行されて、同じコードセグメントにアクセスしている。<br> <syntaxhighlight lang="c++"> // 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(); } </syntaxhighlight> <br> <code>QThread</code>クラスの<code>start</code>メソッドは、派生クラスでオーバーライドした<code>run</code>メソッドを呼び出すことにより、スレッドの実行を開始する。<br> <syntaxhighlight lang="c++"> // 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 </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 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; } } </syntaxhighlight> <br><br> == スレッドの作成(QtConcurrent) == <code>QtConcurrent</code>とは、ミューテックス、読み込み / 書き込みロック、待機条件、セマフォ等の低機能のスレッドプリミティブを使用せずに、<br> マルチスレッドを作成できる高機能APIである。<br> <br> <code>QtConcurrent</code>を使用して作成したスレッドは、使用可能なプロセッサコアの数に応じて、使用されるスレッドの数を自動的に調整する。<br> つまり、現在作成されているアプリケーションは、将来マルチコアシステムに導入された場合でも拡張を続ける。<br> <br> 以下の例では、上記のセクションと同じ出力を取得している。<br> 設計者がスレッドを作成する必要はなく、適切なパラメータを使用して関数を定義するだけである。<br> <br> <code>QtConcurrent</code>のAPIは、以下の通りである。<br> * <code>QtConcurrent::run()</code> *: 別のスレッドで関数を実行する。 * <code>QFuture</code>クラス *: 非同期処理の結果を表す。 *: <code>QFuture</code>クラスは、<code>cancel()</code>、<code>pause()</code>、<code>resume()</code>等の実行中の計算と対話する方法も提供している。 *: ただし、以下の例では、<code>QtConcurrent::run()</code>が<code>QFuture</code>クラスを返すため、これを行うことはできない。 *: これについては、<code>QtConcurrent::mappedReduced()</code>の後半のセクションを参照すること。 // .proファイル # QT項目にconcurrentを追記する QT += concurrent <br> <syntaxhighlight lang="c++"> // main.cppファイル #include <QCoreApplication> #include <qtconcurrentrun.h> #include <QThread> #ifndef QT_NO_CONCURRENT void myRunFunction(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); QFuture<void> t1 = QtConcurrent::run(myRunFunction, QString("A")); QFuture<void> t2 = QtConcurrent::run(myRunFunction, QString("B")); QFuture<void> t3 = QtConcurrent::run(myRunFunction, QString("C")); t1.waitForFinished(); t2.waitForFinished(); t3.waitForFinished(); return a.exec(); } #else #include <QLabel> int main(int argc, char *argv[]) { QApplication app(argc, argv); QString text("Qt Concurrent is not yet supported on this platform"); QLabel *label = new QLabel(text); label->setWordWrap(true); label->show(); qDebug() << text; app.exec(); } #endif </syntaxhighlight> <br><br> == スレッドのプライオリティ == 優先度に応じて、スレッドがどのように動作するかを記載する。<br> <br> 一般的に、スレッドの優先順位を設定する部分を除いて、[[Qtの基礎_-_マルチスレッド#スレッドの作成(非推奨)|"スレッドの作成(非推奨)"セクション]]と同じである。<br> 優先度は、<code>QThread</code>クラスの<code>start</code>メソッドに渡されるパラメータにより設定される。<br> その定義は、以下のようになる。<br> <syntaxhighlight lang="c++"> void QThread::start(Priority priority = InheritPriority) </syntaxhighlight> <br> <code>start</code>メソッドは、<code>run</code>メソッドを呼び出してスレッドの実行を開始する。(既にスレッドが実行されている場合、<code>start</code>メソッドはNOP)<br> OSは、優先度パラメータにしたがってスレッドをスケジュールする。<br> <br> 優先度パラメータの効果は、OSのスケジューリングポリシーによって異なる。<br> 特に、スレッドの優先順位をサポートしていないシステムでは、優先順位は無視される。<br> <center> 表. enum QThread::Priority<br> {| class="wikitable" style="background-color:#fefefe;" |- ! style="background-color:#00ffff;" | 定数 ! style="background-color:#00ffff;" | 値 ! style="background-color:#00ffff;" | 説明 |- | QThread::IdlePriority || 0 || 他のスレッドが実行されていない場合にのみ、スケジュールされる。 |- | QThread::LowestPriority || 1 || LowPriorityよりも少ない頻度でスケジュールされる。 |- | QThread::NormalPriority || 3 || OSの標準の優先度。 |- | QThread::HighestPriority || 5 || HighPriorityよりも頻繁にスケジュールされる。 |- | QThread::QThread::InheritPriority || 7 || 作成したスレッドと同じ優先度を使用する。(初期状態) |} </center> <br> <syntaxhighlight lang="c++"> // 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(); } </syntaxhighlight> <br> 上記のサンプルコードの実行結果から、SUSEのスケジューラは、スレッドの優先順位を尊重しているようである。<br> プライオリティの低い"A"スレッドが最後に終了している。<br> ...略 "A" 96 "A" 97 "A" 98 "A" 99 "A" 100 <br> 優先順位の逆転の問題がある場合、 [http://www.bogotobogo.com/cplusplus/multithreaded.php#priority_inversion 優先順位の逆転]を参照すること。<br> <br><br> == QMutexクラス == QMutexクラスは、スレッド間のアクセスシリアル化を提供する。 QMutexの目的は、オブジェクト、データ構造、ソースコードのセクションを保護して、1度に1つのスレッドのみがアクセスできるようにする。(同期) 一般的に、QMutexLockerでミューテックスを使用することが最適である。 これにより、ロックとロック解除が一貫して実行されるようになる。 <syntaxhighlight lang="c++"> QMutex::QMutex(RecursionMode mode = NonRecursive) </syntaxhighlight> <br> # QMutexクラスのインスタンスを生成して、新しいミューテックスを構築する。<br>ミューテックスは、ロック解除された状態で生成される。 # ミューテックスのモードがQMutex::Recursiveの場合、スレッドは同じミューテックスを複数回ロックでき、<br>対応する数のunlockメソッドの呼び出しが行われるまで、ミューテックスのロックは解除されない。 # それ以外の場合、スレッドはミューテックスを1回だけロックできる。<br>モードを指定しない場合は、QMutex::NonRecursiveが指定される。 <br> <center> 表. enum QMutex::RecursionMode<br> {| class="wikitable" style="background-color:#fefefe;" |- ! style="background-color:#00ffff;" | 定数 ! style="background-color:#00ffff;" | 値 ! style="background-color:#00ffff;" | 説明 |- | QMutex::Recursive || 1 || スレッドは、同じミューテックスを複数回ロックでき、<br>対応する数のunlockメソッドの呼び出しが行われるまで、ミューテックスはロック解除されない。 |- | QMutex::NonRecursive || 0 || スレッドは、ミューテックスを1回だけロックできる。 |} </center> <br> 以下の例では、スレッドの動作を制御するためのメンバ変数bStopを定義している。<br> 変数bStopがtrueの場合、スレッドはループから抜ける。<br> したがって、1つのスレッドからのみアクセスする必要があり、ミューテックスのロックを使用している。<br> <syntaxhighlight lang="c++"> // 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(); } </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 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 </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 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; } } </syntaxhighlight> <br> # 出力 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 <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - マルチスレッド
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse