リエントランシーとスレッドセーフ
このドキュメントでは、クラスや関数がマルチスレッド・アプリケーションでどのように使用できるかを示すために、リエントラントと スレッドセーフという用語を使用しています:
- スレッドセーフな関数は、共有データを使用している場合でも、共有データへのすべての参照がシリアライズされているため、複数のスレッドから同時に呼び出すことができます。
- リエントラント関数も複数のスレッドから同時に呼び出すことができますが、それぞれの呼び出しが独自のデータを使用する場合に限られます。
したがって、スレッドセーフな関数は常にリエントラントですが、リエントラントな関数は必ずしもスレッドセーフではありません。
その延長として、そのクラスのメンバ関数が複数のスレッドから安全に呼び出せる場合、各スレッドがそのクラスの異なるインスタンスを使用する限り、そのクラスはリエントラントであると言われます。そのクラスのメンバ関数が複数のスレッドから安全に呼び出せる場合、たとえすべてのスレッドがそのクラスの同じインスタンスを使用していても、そのクラスはスレッドセーフです。
注意: Qt のクラスがスレッドセーフとして文書化されているのは、複数のスレッドで使用されることを想定している場合のみです。スレッドセーフまたはリエントラントとしてマークされていない関数は、異なるスレッドから使用するべきではありません。クラスがスレッドセーフまたはリエントラントとしてマークされていない場合、そのクラスの特定のインスタンスに異なるスレッドからアクセスすべきではありません。
再入可能性
C++クラスはリエントラントであることが多い。他のスレッドが同時に同じクラスのインスタンスのメンバ関数を呼び出さない限り、どのスレッドでも再入可能なクラスのインスタンスのメンバ関数を呼び出すことができます。例えば、以下のCounter
クラスはリエントラントです:
class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; };
複数のスレッドがデータ・メンバn
を変更しようとすると、結果は未定義になるからです。これは、++
と--
演算子が必ずしもアトミックではないからです。実際、これらは通常3つのマシン命令に拡張される:
- 変数の値をレジスタにロードする。
- レジスタの値をインクリメントまたはデクリメントする。
- レジスタの値をメイン・メモリにストアする。
スレッドAとスレッドBが同時に変数の古い値をロードし、レジスタをインクリメントし、それをストアし直すと、互いに上書きすることになり、変数は1回だけインクリメントされる!
スレッドセーフティ
明らかに、アクセスは直列化されなければならない:スレッドBが同じステップを実行する前に、スレッドAはステップ1、2、3を中断することなく(アトミックに)実行しなければならない。クラスをスレッドセーフにする簡単な方法は、データ・メンバへのすべてのアクセスをQMutex で保護することである:
class Counter { public: Counter() { n = 0; } void increment() { QMutexLocker locker(&mutex); ++n; } void decrement() { QMutexLocker locker(&mutex); --n; } int value() const { QMutexLocker locker(&mutex); return n; } private: mutable QMutex mutex; int n; };
QMutexLocker クラスは、コンストラクタでミューテックスを自動的にロックし、デストラクタが呼び出されたとき、関数の最後でロックを解除します。ミューテックスをロックすることで、異なるスレッドからのアクセスも確実にシリアライズされます。value()
でミューテックスをロックしたりアンロックしたりする必要があるため、mutex
データ・メンバはmutable
という修飾子で宣言されています。
Qt クラスに関する注意
QString Qt の多くのクラスはリエントラントですが、スレッドセーフにはなっていません。スレッドセーフにすると、QMutex のロックとアンロックを繰り返すという余分なオーバーヘッドが発生するからです。複数のスレッドから同時にQString の異なるインスタンスに安全にアクセスすることはできますが、複数のスレッドから同時にQString の同じインスタンスに安全にアクセスすることはできません(QMutex でアクセスを保護しない限り)。
Qtのいくつかのクラスや関数はスレッドセーフです。これらは主にスレッド関連のクラス(例:QMutex )と基本的な関数(例:QCoreApplication::postEvent())です。
注: マルチスレッド領域の用語は完全に標準化されているわけではありません。POSIXはリエントラントとスレッドセーフの定義を使用しているが、C APIでは多少異なっている。他のオブジェクト指向 C++ クラスライブラリを Qt で使用する場合は、定義が理解されていることを確認してください。
©2024 The Qt Company Ltd. ここに含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。