web-dev-qa-db-ja.com

moveToThread vs QtのQThreadからの派生

moveToThreadは、サブクラス化QThreadよりも優先する必要があるのはいつですか?

このリンク は、両方の方法が機能することを示しています。これらの2つから何を使用するかをどの基準で決定する必要がありますか?

25
Aquarius_Girl

2つの方法の違いに焦点を当てます。すべてのユースケースに当てはまる一般的な答えはありません。そのため、それらが何であるかを正確に理解して、あなたのケースに最適なものを選ぶのは良いことです。

MoveToThread()の使用

moveToThread()は、オブジェクトのスレッドアフィニティを制御するために使用されます。これは、基本的にスレッドの設定(またはより良いQtイベントループ)。オブジェクトはそこからシグナルを発行し、そのスロットが実行されます。

リンクしたドキュメントに示されているように、これは別のスレッドでコードを実行するために使用でき、基本的にダミーワーカーを作成し、実行するコードを記述しますapublic slot(この例ではdoWork()スロット)を使用し、次にmoveToThreadを別のイベントループに移動します。

次に、そのスロットに接続されている信号が発生します。シグナルを送信するオブジェクト(例ではController)は別のスレッドにあり、シグナルはキュー接続でdoWorkメソッドに接続されているため、doWorkメソッドはワーカースレッドで実行されます。

ここで重要なのは、ワーカースレッドによって実行される新しいイベントループを作成していることです。したがって、doWorkスロットが開始されると、イベントループ全体が終了するまでビジー状態になり、これは着信信号がキューに入れられることを意味します。

QThread()のサブクラス化

Qtのドキュメントで説明されているもう1つの方法は、QThreadのサブクラス化です。この場合、QThread :: run()メソッドのデフォルト実装をオーバーライドして、イベントループを作成し、別の何かを実行します。

いくつかの落とし穴がありますが、このアプローチ自体に問題はありません。

まず、run()メソッドはそのクラスで実際に別のスレッドで実行される唯一のメソッドであるため、安全でないコードを書くのは非常に簡単です。

例として、コンストラクターで初期化し、run()メソッドで使用するメンバー変数がある場合、メンバーは呼び出し元のスレッドで初期化され、次に新しいスレッドで使用されます。

呼び出し元から、またはrun()内から呼び出すことができる任意のパブリックメソッドについても同じことが言えます。

また、スロットは呼び出し側のスレッドから実行されます(moveToThread(this)として本当に奇妙なことをしない限り)、さらに混乱が生じます。

だから、それは可能ですが、あなたは本当にあなた自身がこのアプローチであなた自身にあり、あなたは特別な注意を払う必要があります。

その他のアプローチ

もちろん、必要に応じて、両方のアプローチに代わるものがあります。 GUIスレッドの実行中にコードをバックグラウンドで実行するだけの場合は、 QtConcurrent :: run() の使用を検討してください。

ただし、QtConcurrentはグローバルQThreadPoolを使用することに注意してください。プール全体がビジーの場合(つまり、プールに使用可能なスレッドがない場合)、コードはすぐには実行されません。

別の方法として、少なくともC++ 11を使用している場合は、 std :: thread などの低レベルのAPIを使用します。

18

開始点として、どちらも使用しないでください。ほとんどの場合、非同期で実行したい作業単位があります。使用する - QtConcurrent::run そのため。

イベントに反応したりタイマーを使用したりするオブジェクトがある場合、それはQObjectであり、非ブロッキングでスレッドに移動する必要があります。おそらく他のオブジェクトと共有されます。

このようなオブジェクトは、ブロッキングAPIをラップすることもできます。

QThreadのサブクラス化は、実際には必要ありません。 QFileをサブクラス化するようなものです。 QThreadはスレッドハンドルです。システムリソースをラップします。オーバーロードは少しばかげています。

9

QThreadは低レベルのスレッド抽象化です。最初に高レベルのAPIを見てください QtConcurrent モジュールと QRunnable

これらのどれもあなたに適していない場合は、 この古い記事 を読んで、QThreadの使用方法を説明します。このスレッドで実行されるスレッドとタスクを個別のオブジェクトとして考え、それらを一緒に混ぜないでください。

したがって、カスタム、特定、または拡張スレッドラッパーを記述する必要がある場合は、QThreadをサブクラス化する必要があります。

シグナルとスロットを持つQObject派生クラスがある場合は、そのクラスでmoveToThreadを使用します。

他の場合では、QtConcurrent、QRunnable、QThreadPollを使用します。

3
Jeka

単純な答えは常にです。オブジェクトをスレッドに移動すると:

  • コードのテストを書くのは簡単です
  • コードのリファクタリングは簡単です(スレッドを使用できますが、必須ではありません)。
  • スレッドの機能とビジネスロジックを混在させない
  • オブジェクトの寿命に問題はありません

QThreadをサブクラス化するとき

  • テストを書くのは難しい
  • オブジェクトのクリーンアッププロセスは非常に混乱し、奇妙なエラーを引き起こす可能性があります。

Qtブログからの問題の完全な説明があります: あなたはそれを間違っています...

QtConcurrent::runも非常に便利です。

デフォルトでは、他のスレッドオブジェクトが割り当てられているスレッドから信号が送信されると、スロットは踏み板の間をジャンプしようとします。詳細は Qt :: ConnectionType のドキュメントを参照してください。

2
Marek R