Qt におけるマルチスレッド技術

Qt にはスレッドを扱うためのクラスや関数がたくさんあります。以下は、Qt プログラマがマルチスレッドアプリケーションを実装するために使用できる 4 つの異なるアプローチです。

QThread:オプションのイベントループを持つ低レベル API

QThread は Qt におけるすべてのスレッド制御の基礎です。各 インスタンスは、1 つのスレッドを表し、制御します。QThread

QThread QThread は直接インスタンス化することも、サブクラス化することもできます。 をインスタンス化すると並列イベントループが提供され、 スロットをセカンダリスレッドで呼び出すことができます。 をサブクラス化すると、アプリケーションはイベント・ループを開始する前に新しいスレッドを初期化したり、イベント・ループなしで並列コードを実行できるようになります。QThread QObject QThread

QThread の使用方法については、QThread class referenceスレッド作成例を参照してください。

QThreadPool と QRunnable:スレッドの再利用

スレッドを頻繁に作成したり破棄したりするには、コストがかかります。このオーバーヘッドを減らすために、既存のスレッドを新しいタスクに再利用することができます。QThreadPool は、再利用可能な QThreads のコレクションです。

QThreadPool のスレッドの 1 つでコードを実行するには、QRunnable::run() を再実装し、サブクラス化されたQRunnable をインスタンス化します。QRunnableQThreadPool の実行キューに入れるには、QThreadPool::start() を使用します。スレッドが使用可能になると、QRunnable::run() 内のコードがそのスレッドで実行されます。

各 Qt アプリケーションにはグローバル・スレッド・プールがあり、QThreadPool::globalInstance() からアクセスできます。このグローバル・スレッド・プールは、CPUのコア数に基づいて最適なスレッド数を自動的に維持します。しかし、QThreadPool を個別に作成し、明示的に管理することもできます。

Qt コンカレント:高レベル API の使用

Qt Concurrentモジュールは、一般的な並列計算パターンである map、filter、reduce を扱う高水準関数を提供します。QThreadQRunnable を使用するのとは異なり、これらの関数はミューテックスやセマフォのような低レベルのスレッドプリミティブを使用する必要はありません。代わりに、QFuture オブジェクトを返します。このオブジェクトは、準備ができたときに関数の結果を取得するために使用できます。QFuture は、計算の進行状況を照会したり、計算を一時停止/再開/キャンセルしたりするためにも使用できます。利便性のために、QFutureWatcher はシグナルとスロットを介してQFutureと相互作用することができます。

Qt Concurrent の map、filter、reduce アルゴリズムは、利用可能なすべてのプロセッサコアに自動的に計算を分散させるので、今日作成されたアプリケーションは、後でコア数の多いシステムにデプロイされても拡張し続けることができます。

このモジュールは、別のスレッドで任意の関数を実行できるQtConcurrent::run() 関数も提供します。しかし、QtConcurrent::run ()は、map、filter、reduce関数で利用可能な機能のサブセットしかサポートしていない。QFuture は、関数の戻り値を取得したり、スレッドが実行中かどうかをチェックするために使用できます。ただし、QtConcurrent::run ()の呼び出しは、1つのスレッドのみを使用し、一時停止/再開/キャンセルはできず、進行状況を問い合わせることもできません。

個々の関数の詳細については、Qt Concurrentモジュールのドキュメントを参照してください。

WorkerScript を参照してください:QML でのスレッド処理

WorkerScript QML タイプは JavaScript コードを GUI スレッドと並行して実行させます。

WorkerScript インスタンスには.js スクリプトを1つアタッチすることができます。WorkerScript.sendMessage() が呼び出されると、スクリプトは別のスレッドで実行されます (QML context も別です)。スクリプトの実行が終了すると、WorkerScript.onMessage() シグナル・ハンドラを呼び出すGUIスレッドに応答を送り返すことができます。

WorkerScript を使用するのは、別のスレッドに移動したワーカーQObject を使用するのと似ている。データはシグナルを介してスレッド間で転送されます。

スクリプトの実装方法の詳細や、スレッド間で受け渡し可能なデータタイプのリストについては、WorkerScript のドキュメントを参照してください。

適切なアプローチの選択

上記で示したように、Qt はスレッドアプリケーションを開発するためのさまざまなソリューションを提供します。あるアプリケーションに適したソリューションは、新しいスレッドの目的とスレッドの寿命に依存します。以下に、Qt のスレッド技術の比較と、いくつかの使用例に対する推奨ソリューションを示します。

ソリューションの比較

機能QThreadQRunnable そしてQThreadPoolQtConcurrent::run()Qt コンカレント(Map、Filter、Reduce)WorkerScript
言語C++C++C++C++QML
スレッド優先度指定可能ありあり
スレッドはイベントループを実行できる
スレッドはシグナルを通してデータ更新を受け取ることができるはい (ワーカーによって受信QObject)はい (WorkerScript で受信)
スレッドはシグナルを使って制御できるはい(QThread によって受信)はい(QFutureWatcher によって受信)
スレッドはQFuture部分的にあり
一時停止/再開/キャンセル機能内蔵あり

使用例

スレッドの寿命操作解決方法
1回の呼び出し別のスレッド内で新しい一次関数を実行します。Qt はさまざまなソリューションを提供しています:
コール別のスレッド内で既存の関数を実行し、その戻り値を取得する。QtConcurrent::run() を使って関数を実行する。関数が返ったらQFutureWatcherfinished() シグナルを発行させ、QFutureWatcher::result() をコールして関数の戻り値を取得する。
1回の呼び出し利用可能なすべてのコアを使用して、コンテナのすべてのアイテムに対して操作を実行する。例えば、画像のリストからサムネイルを生成します。Qt ConcurrentのQtConcurrent::filter ()関数を使用してコンテナの要素を選択し、QtConcurrent::map ()関数を使用して各要素に操作を適用します。出力を1つの結果にまとめるには、代わりにQtConcurrent::filteredReduced() とQtConcurrent::mappedReduced() を使用します。
ワンコール/パーマネント純粋なQMLアプリケーションで長い計算を行い、結果の準備ができたらGUIを更新します。計算コードを.js スクリプトに記述し、WorkerScript インスタンスにアタッチします。WorkerScript.sendMessage() を呼び出し、新しいスレッドで計算を開始します。スクリプトもsendMessage()を呼び出して、結果をGUIスレッドに戻します。結果をonMessage で処理し、そこでGUIを更新する。
パーマネント別のスレッドに、要求に応じて別のタスクを実行したり、新しいデータを受け取ったりできるオブジェクトを常駐させます。QObject をサブクラス化してワーカーを作成します。このワーカーオブジェクトとQThread をインスタンス化します。 ワーカーを新しいスレッドに移動します。キューイングされたシグナルスロット接続を介して、ワーカーオブジェクトにコマンドやデータを送る。
パーマネントスレッドがシグナルやイベントを受け取る必要のない別のスレッドで、高価な処理を繰り返し実行する。QThread::run() の再実装の中に直接無限ループを書く。イベント・ループなしでスレッドを開始する。スレッドにシグナルを発行させ、GUIスレッドにデータを送り返す。

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。