C++ 標準ライブラリ (libCstd -library=Cstd) は、いくつかのロケールを除けばマルチスレッドで使用しても安全なライブラリで、このライブラリの内部は、マルチスレッド環境で正しく機能することが保証されています。ただし、依然、スレッド間で共有するライブラリオブジェクト周りには注意を払う必要があります。setlocale(3C) および attributes(5) のマニュアルページを参照してください。
たとえば、文字列をインスタンス化し、この文字列を新しく生成したスレッドに参照で渡した場合を考えてみましょう。この文字列への書き込みアクセスはロックする必要があります。なぜなら、同じ文字列オブジェクトを、プログラムが複数のスレッドで明示的に共有しているからです。この処理を行うために用意されたライブラリの機能については後述します。
これに対して、この文字列を新しいスレッドに値で渡した場合は、ロックについて考慮する必要はありません。このことは、Rogue Wave の「書き込み時コピー」機能により、2 つのスレッドの別々の文字列が同じ表現を共有している場合にも当てはまります。このような場合のロックは、ライブラリが自動的に処理します。プログラム自身でロックを行う必要があるのは、スレッド間での参照渡しや、大域オブジェクトや静的オブジェクトを使用して、同じオブジェクトを複数のスレッドから明示的に使用できるようにした場合だけです。
ここからは、複数のスレッドが存在する場合の動作を保証するために、C++ 標準ライブラリの内部で使用されるロック (同期) 機能について説明します。
マルチスレッドでの安全性を実現する機能は、2 つの同期クラス、_RWSTDMutex と _RWSTDGuard によって提供されます。
_RWSTDMutex クラスは、プラットフォームに依存しないロック機能を提供します。このクラスには、次のメンバー関数があります。
void acquire()— 自分自身に対するロックを獲得する。または、このロックを獲得できるまでブロックする。
void release()— 自分自身に対するロックを解除する。
class _RWSTDMutex { public: _RWSTDMutex (); ~_RWSTDMutex (); void acquire (); void release (); }; |
_RWSTDGuard クラスは、_RWSTDMutex クラスのオブジェクトをカプセル化するための便利なラッパークラスです。_RWSTDGuard クラスのオブジェクトは、自分自身のコンストラクタの中で、カプセル化された相互排他ロック (mutex) を獲得しようとします。エラーが発生した場合は、このコンストラクタは std::exception から派生している ::thread_error 型の例外を送出します。獲得された相互排他ロックは、このオブジェクトのデストラクタの中で解除されます。このデストラクタは例外を送出しません。
class _RWSTDGuard { public: _RWSTDGuard (_RWSTDMutex&); ~_RWSTDGuard (); }; |
さらに、_RWSTD_MT_GUARD(mutex) マクロ (従来の _STDGUARD) を使用すると、マルチスレッドの構築時にだけ _RWSTDGuard クラスのオブジェクトを生成できます。生成されたオブジェクトは、そのオブジェクトが定義されたコードブロックの残りの部分が、複数のスレッドで同時に実行されないようにします。単一スレッドの構築時には、このマクロは空白の式に展開されます。
これらの機能は、次のように使用します。
#include <rw/stdmutex.h> // // 複数のスレッドで共有する整数 // int I; // // I の更新の同期をとるために使用する相互排他ロック (mutex) // _RWSTDMutex I_mutex; // // I を 1 だけ増分する。_RWSTDMutex を直接使用。 // void increment_I () { I_mutex.acquire(); // mutex をロック I++; I_mutex.release(); // mutex のロックを解除 } // // I を 1 だけ減分する。_RWSTDGuard を使用。 // void decrement_I () { _RWSTDGuard guard(I_mutex); // I_mutex のロックを獲得 --I; // // I のロックは guard のデストラクタが呼び出されたときに解除される // } |