10.3 C++ 標準ライブラリのオブジェクトのスレッド間での共有
10.4 マルチスレッド環境での従来の iostream の使用
10.4.1 マルチスレッドで使用しても安全な iostream ライブラリの構成
10.4.1.2 マルチスレッドで使用しても安全な libC ライブラリを使用したコンパイルとリンク
10.4.1.3 マルチスレッドで使用しても安全な iostream の制約
10.4.1.4 マルチスレッドで使用しても安全なクラスのパフォーマンスオーバーヘッドの削減
コンパイラには、SPARC プロセッサと x86 プロセッサ用のさまざまなメモリーバリアー組み込み関数を定義するヘッダーファイル mbarrier.h が用意されています。これらの組み込み関数は、開発者が独自の同期プリミティブを使用してマルチスレッドコードを記述するために使用できます。これらの組み込み関数がいつ必要になるか、また、特定の状況で必要かどうかを判断するために、ユーザーは使用しているプロセッサのドキュメントを参照することが推奨されます。
mbarrier.h によりサポートされるメモリーオーダリング組み込み関数
__machine_r_barrier() — これは、read バリアーです。これにより、バリアー前のすべてのロード操作が、バリアー後のすべてのロード操作の前に完了します。
__machine_w_barrier() — これは、write バリアーです。これにより、バリアー前のすべての格納操作が、バリアー後のすべての格納操作の前に完了します。
__machine_rw_barrier() — これは、read—write バリアーです。これにより、バリアー前のすべてのロードおよび格納操作が、バリアー後のすべてのロードおよび格納操作の前に完了します。
__machine_acq_barrier() — これは、acquire セマンティクスを持つバリアーです。これにより、バリアー前のすべてのロード操作が、バリアー後のすべてのロードおよび格納操作の前に完了します。
__machine_rel_barrier() — これは、release セマンティクスを持つバリアーです。これにより、バリアー前のすべてのロードおよび格納操作が、バリアー後のすべての格納操作の前に完了します。
__compiler_barrier() — コンパイラが、バリアーを越えてメモリーアクセスを移動しないようにします。
__compiler_barrier() 組み込み関数を除くすべてのバリアー組み込み関数は、メモリーオーダリング組み込み関数を生成します。x86 では、mfence、sfence、または lfence 組み込み関数で、SPARC プラットフォームでは membar 組み込み関数です。
__compiler_barrier() 組み込み関数は、命令を生成せず、代わりに今後メモリー操作を開始する前にそれまでのメモリー操作をすべて完了する必要があることをコンパイラに通知します。この実際の結果として、ローカルでないすべての変数、および static 記憶クラス指定子を持つローカル変数が、バリアー前のメモリーに再度格納されてバリアー後に再ロードされるため、コンパイラではバリアー前のメモリー操作とバリアー後のメモリー操作が混在することはありません。ほかのすべてのバリアーには、__compiler_barrier() 組み込み関数の動作が暗黙的に含まれています。
たとえば、次のコードでは、__compiler_barrier() 組み込み関数が存在しているためコンパイラによる 2 つのループのマージが止まります。
#include "mbarrier.h" int thread_start[16]; void start_work() { /* Start all threads */ for (int i=0; i<8; i++) { thread_start[i]=1; } __compiler_barrier(); /* Wait for all threads to complete */ for (int i=0; i<8; i++) { while (thread_start[i]==1){} } }