スレッドの同期

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

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

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

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

QReadWriteLock は、「読み取り」と「書き込み」のアクセスを区別する点を除けば、 に似ている。データが書き込まれていないときは、複数のスレッドが同時に読んでも安全である。 、複数の読者が交代で共有データを読むことを余儀なくされるが、 、同時に読むことができるため、並列性が向上する。QMutex QMutex QReadWriteLock

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

QWaitCondition セマフォは相互排他を強制するのではなく、条件変数を提供することによってスレッドを同期させます。他のプリミティブがリソースがアンロックされるまでスレッドを待たせるのに対し、 は特定の条件が満たされるまでスレッドを待たせる。待機中のスレッドに処理を続行させるには、 ()を呼び出してランダムに選択したスレッドを1つ起こすか、 ()を呼び出してすべてのスレッドを同時に起こす。QWaitCondition wakeOne wakeAll待機条件を使用したProducerとConsumerの例では、 の代わりに を使用してProducerとConsumerの問題を解決する方法を示しています。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() およびThreads and QObjectsも参照してください

© 2025 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.