10.4 マルチスレッド環境での従来の iostream の使用
10.4.1 マルチスレッドで使用しても安全な iostream ライブラリの構成
10.4.1.2 マルチスレッドで使用しても安全な libC ライブラリを使用したコンパイルとリンク
10.4.1.3 マルチスレッドで使用しても安全な iostream の制約
10.4.1.4 マルチスレッドで使用しても安全なクラスのパフォーマンスオーバーヘッドの削減
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> // // An integer shared among multiple threads. // int I; // // A mutex used to synchronize updates to I. // _RWSTDMutex I_mutex; // // Increment I by one. Uses an _RWSTDMutex directly. // void increment_I () { I_mutex.acquire(); // Lock the mutex. I++; I_mutex.release(); // Unlock the mutex. } // // Decrement I by one. Uses an _RWSTDGuard. // void decrement_I () { _RWSTDGuard guard(I_mutex); // Acquire the lock on I_mutex. --I; // // The lock on I is released when destructor is called on guard. // }