スレッドと QObjects
QThread は を継承しています。スレッドが実行を開始または終了したことを示すシグナルを発し、いくつかのスロットも提供します。QObject
さらに興味深いのは、QObjects を複数のスレッドで使用したり、他のスレッドのスロットを呼び出すシグナルを発したり、他のスレッドで「生きている」オブジェクトにイベントをポストしたりできることです。これは、各スレッドが独自のイベント・ループを持つことが許されているから可能なのだ。
Qオブジェクトの再入可能性
QObject はリエントラントである。また、 、 、 、 などの非GUIサブクラスの多くもリエントラントであるため、これらのクラスを複数のスレッドから同時に使用することが可能です。あるスレッドでオブジェクトを作成し、別のスレッドからその関数を呼び出しても動作は保証されません。注意すべき3つの制約があります:QTimer QTcpSocket QUdpSocket QProcess
- あるスレッドでオブジェクトを作成し、別のスレッドからその関数を呼び出すことは保証されていません。QObject の子は、常に親が作成されたスレッドで作成されなければなりません。これは特に、QThread オブジェクト(
this
)を、スレッド内で作成されたオブジェクトの親として渡してはならないことを意味します(QThread オブジェクト自体は別のスレッドで作成されているため)。 - イベント・ドリブン・オブジェクトは、単一のスレッドでのみ使用できます。 network module例えば、object's thread 以外のスレッドでタイマーを開始したり、ソケットを接続したりすることはできません。
- スレッド内で作成されたすべてのオブジェクトは、QThread を削除する前に確実に削除する必要があります。これは、run() の実装でスタック上にオブジェクトを作成することで、簡単に行うことができます。
QObject はリエントラントですが、GUIクラス、特にQWidget とそのサブクラスはリエントラントではありません。これらはメイン・スレッドからのみ使用できる。先に述べたように、QCoreApplication::exec ()もそのスレッドから呼び出されなければならない。
実際には、メイン・スレッド以外のスレッドでGUIクラスを使用できないことは、時間のかかる処理を別のワーカースレッドに置き、ワーカースレッドが終了したときにメイン・スレッドで結果を画面に表示することで簡単に回避できる。これは、マンデルブロの例とブロック・フォーチュン・クライアントの例の実装に使用されたアプローチです。
一般的に、QApplication の前にQObjectを作成することはサポートされておらず、プラットフォームによっては終了時に奇妙なクラッシュを引き起こす可能性があります。これは、QObject の静的インスタンスもサポートされていないことを意味します。適切に構造化されたシングルスレッドまたはマルチスレッドのアプリケーションでは、QApplication が最初に作成され、QObject が最後に破棄されるようにする必要があります。
スレッドごとのイベント・ループ
各スレッドは独自のイベント・ループを持つことができる。最初のスレッドはQCoreApplication::exec() を使用してイベント・ループを開始し、シングル・ダイアログ GUI アプリケーションではQDialog::exec() を使用することもある。他のスレッドは、QThread::exec ()を使用してイベント・ループを開始できます。QCoreApplication と同様に、QThread はexit(int) 関数とquit() スロットを提供する。
スレッド内のイベント・ループは、イベント・ループの存在を必要とする特定の非GUI Qtクラス(QTimer 、QTcpSocket 、QProcess など)をスレッドで使用することを可能にします。また、任意のスレッドからのシグナルを特定のスレッドのスロットに接続することも可能になります。これについては、以下の「スレッドをまたがるシグナルとスロット」のセクションで詳しく説明する。
QObject インスタンスは、それが作成されたスレッドに住んでいると言われる。そのオブジェクトに対するイベントは、そのスレッドのイベントループによってディスパッチされる。QObject が存在するスレッドは、QObject::thread() を使って確認できる。
QObject::moveToThread ()関数は、オブジェクトとその子のスレッド親和性を変更します(親を持つオブジェクトは移動できません)。
オブジェクトを所有しているスレッド以外のスレッドからQObject に対してdelete
を呼び出す(または他の方法でオブジェクトにアクセスする)ことは、そのオブジェクトがその時点でイベントを処理していないことを保証しない限り、安全ではありません。代わりにQObject::deleteLater ()を使用すると、DeferredDelete イベントがポストされ、オブジェクトのスレッドのイベントループが最終的にピックアップします。デフォルトでは、QObject を所有するスレッドは、QObject を作成するスレッドですが、QObject::moveToThread() が呼び出された後はそうではありません。
イベント・ループが実行されていない場合、イベントはオブジェクトに配信されません。たとえば、あるスレッドでQTimer オブジェクトを作成し、exec() を一度も呼び出さなかった場合、QTimer がtimeout() シグナルを発信することはありません。deleteLater() を呼んでも動作しません。(これらの制限はメイン・スレッドにも適用されます)。
スレッドセーフ関数QCoreApplication::postEvent() を使えば、いつでもどのスレッドのどのオブジェクトに対しても、手動でイベントをポストすることができます。イベントは、オブジェクトが作成されたスレッドのイベントループによって自動的にディスパッチされます。
イベント・フィルターはすべてのスレッドでサポートされていますが、監視オブジェクトは監視オブジェクトと同じスレッドに存在しなければならないという制限があります。同様に、QCoreApplication::sendEvent ()(postEvent ()とは異なり)は、その関数が呼び出されたスレッドに住んでいるオブジェクトにイベントをディスパッチするためにのみ使用できます。
他のスレッドからのQObjectサブクラスへのアクセス
QObject およびそのすべてのサブクラスはスレッドセーフではありません。これには、イベント配信システム全体が含まれます。他のスレッドからオブジェクトにアクセスしている間に、イベントループが のサブクラスにイベントを配信している可能性があることに留意することが重要です。QObject
現在のスレッドに存在しないQObject サブクラスで関数を呼び出していて、そのオブジェクトがイベントを受け取る可能性がある場合は、QObject サブクラスの内部データへのすべてのアクセスをミューテックスで保護する必要があります。
他のオブジェクトと同様に、QThread オブジェクトは、QThread::run() が呼び出されたときに生成されるスレッドではなく、オブジェクトが作成されたスレッドに存在します。メンバ変数をミューテックスで保護しない限り、QThread サブクラスでスロットを提供するのは一般的に安全ではありません。
一方、QThread::run() の実装から安全にシグナルを発信することができます。シグナルの発信はスレッドセーフだからです。
スレッド間のシグナルとスロット
Qtはこれらのシグナルとスロットの接続タイプをサポートしています:
- Auto Connection (デフォルト) 受信オブジェクトがアフィニティを持っているスレッドでシグナルが発信された場合、動作は直接接続と同じになります。それ以外の場合、動作はキュー接続と同じです。"
- Direct Connection シグナルが発信されると、スロットは即座に呼び出されます。スロットはエミッターのスレッドで実行され、レシーバーのスレッドとは限らない。
- Queued Connection スロットは、レシーバのスレッドのイベントループに制御が戻ったときに呼び出されます。スロットは受信側のスレッドで実行されます。
- Blocking Queued Connection スロットはキュー接続と同様に呼び出されますが、スロットが戻るまで現在のスレッドはブロックされます。
注意: 同じスレッド内のオブジェクトを接続するためにこの型を使用すると、デッドロックが発生します。
- Unique Connection 動作は自動接続と同じですが、既存の接続と重複しない場合にのみ接続が行われます。つまり、同じシグナルが同じペアのオブジェクトに対して同じスロットに既に接続されている場合、接続は行われず、connect() は を返します。
false
接続タイプは、connect ()に追加引数を渡すことで指定できる。送信側と受信側が異なるスレッドで動作している場合に直接接続を使用すると、受信側のスレッドでイベントループが実行されている場合に安全でないことに注意してください。
QObject::connect()自体はスレッドセーフである。
マンデルブロの例では、ワーカースレッドとメインスレッド間の通信にキュー接続を使用しています。メイン・スレッドのイベント・ループ(そして結果としてアプリケーションのユーザー・インターフェース)のフリーズを避けるため、マンデルブロ・フラクタル計算はすべて別のワーカー・スレッドで行われる。このスレッドはフラクタルのレンダリングが終わるとシグナルを発する。
同様に、Blocking Fortune Clientの例では、TCPサーバと非同期に通信するために別のスレッドを使用しています。
©2024 The Qt Company Ltd. 本書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。