このページでは

スレッドの同期

スレッドの目的はコードを並行して実行させることですが、スレッドが一時停止して他のスレッドを待たなければならない場合もあります。 たとえば、2 つのスレッドが同時に同じ変数に書き込みを試みた場合、結果は未定義となります。スレッド同士が互いに待機するよう強制するこの原則は、相互排他と呼ばれます。これは、データなどの共有リソースを保護するための一般的な手法です。

Qt は、スレッドを同期させるための低レベルのプリミティブと高レベルのメカニズムの両方を提供しています。

低レベルの同期プリミティブ

QMutex は、相互排他を強制するための基本クラスです。スレッドは、共有リソースにアクセスするためにミューテックスをロックします。ミューテックスがすでにロックされている状態で、別のスレッドがミューテックスのロックを試みると、そのスレッドは、最初のスレッドがタスクを完了してミューテックスのロックを解除するまでスリープ状態になります。

QReadWriteLock はQMutex と似ていますが、「読み取り」と「書き込み」のアクセスを区別するという点が異なります。データへの書き込みが行われていない場合、複数のスレッドが同時にそのデータから読み取っても安全です。QMutex は複数の読み取りスレッドに共有データの読み取りを順番に行うよう強制しますが、QReadWriteLock は同時読み取りを許可するため、並列処理の効率が向上します。

QSemaphore は、QMutex を一般化したもので、一定数の同一のリソースを保護します。対照的に、QMutex は正確に1つのリソースのみを保護します。「セマフォを使用したプロデューサーとコンシューマ」の例は、セマフォの典型的な応用例を示しています。それは、プロデューサーとコンシューマ間の循環バッファへのアクセスを同期させるものです。

QWaitCondition は、相互排他を強制するのではなく、条件変数を提供することでスレッドを同期させます。他のプリミティブでは、リソースのロックが解除されるまでスレッドを待機させますが、QWaitCondition では、特定の条件が満たされるまでスレッドを待機させます。待機中のスレッドを続行させるには、wakeOne()を呼び出してランダムに選択された1つのスレッドをウェイクアップするか、wakeAll()を呼び出してすべてのスレッドを同時にウェイクアップします。 「Wait Conditions を使用したプロデューサーとコンシューマー」の例では、QSemaphore の代わりにQWaitCondition を使用して、プロデューサー・コンシューマー問題を解決する方法を示しています。

注:Qtの同期クラスは 、適切にアラインメントされたポインタの使用に依存しています。たとえば、MSVCではパケット化されたクラスを使用することはできません。

これらの同期クラスを使用することで、メソッドをスレッドセーフにすることができます。ただし、そうすることでパフォーマンスの低下を招くため、Qt のメソッドのほとんどはスレッドセーフになっていません。

リスク

あるスレッドがリソースをロックしたままアンロックしない場合、そのリソースが他のスレッドから恒久的に利用できなくなるため、アプリケーションがフリーズする可能性があります。これは、例えば例外がスローされ、現在の関数がロックを解放せずに強制的に戻り値を出力した場合などに発生し得ます。

これと類似したシナリオとして、デッドロックがあります。例えば、スレッド A がスレッド B がリソースのロックを解除するのを待っているとします。もしスレッド B も、スレッド A が別のリソースのロックを解除するのを待っている場合、両方のスレッドが永遠に待機し続けることになり、アプリケーションがフリーズしてしまいます。

利便性クラス

QMutexLocker、QReadLocker およびQWriteLocker は、QMutex およびQReadWriteLock の使用を容易にする利便性クラスです。これらはコンストラクタでリソースをロックし、デストラクタで自動的にロックを解除します。これらは、QMutex およびQReadWriteLock を使用するコードを簡素化し、リソースが誤って恒久的にロックされてしまう可能性を低減するように設計されています。

高レベルイベントキュー

Qtのイベントシステムは、スレッド間の通信に非常に役立ちます。各スレッドは独自のイベントループを持つことができます。別のスレッドでスロット(またはinvokable のメソッド)を呼び出すには、その呼び出しをターゲットとなるスレッドのイベントループに配置します。これにより、元のスレッドが並行して実行を続けながら、ターゲットとなるスレッドが現在のタスクを完了してからスロットの実行が開始されます。

イベントループに呼び出しを配置するには、キュー化されたシグナル・スロット接続を作成します。 シグナルが発せられるたびに、その引数はイベントシステムによって記録されます。その後、シグナル受信者lives in が属するスレッドが、そのスロットを実行します。あるいは、QMetaObject::invokeMethod()を呼び出すことで、シグナルを使用せずに同じ効果を得ることができます。いずれの場合も、queued connection を使用する必要があります。これは、direct connection がイベントシステムをバイパスし、現在のスレッドでメソッドを即座に実行してしまうためです。

低レベルのプリミティブを使用する場合とは異なり、スレッドの同期にイベントシステムを使用する場合、デッドロックのリスクはありません。ただし、イベントシステムは排他性を強制するものではありません。呼び出し可能なメソッドが共有データにアクセスする場合は、依然として低レベルのプリミティブを用いて保護する必要があります。

とはいえ、Qtのイベントシステムは、暗黙的に共有されるデータ構造と相まって、従来のスレッドロックの代替手段となります。シグナルとスロットのみを使用し、スレッド間で変数が共有されない場合、マルチスレッドプログラムは低レベルのプリミティブを一切使用せずに動作させることができます。

「 QThread::exec()」および「スレッドとQObject」も参照してください 。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.