共有オブジェクトとマルチスレッドで起こる問題の最も簡単な解決方法は、iostream オブジェクトを 1 つのスレッドに対して局所的にして問題自体をなくしてしまうことです。そのためには、次のような方法があります。
オブジェクトをスレッドのエントリ関数内で局所的に宣言します。
オブジェクトをスレッド固有データ内で宣言します。スレッド固有データの使用方法については、thr_keycreate(3T) のマニュアルページを参照してください。
ストリームオブジェクトを特定のスレッド専用にします。オブジェクトのスレッドは規則により非公開になります。
ところが多くの場合 (たとえばデフォルトの共有標準ストリームオブジェクトの場 合)、オブジェクトを特定のスレッドの局所オブジェクトとすることが不可能で、他の解決策を探さなければなりません。
iostream クラスのオブジェクトに対する一連の操作を不可分命令的に実行するには、なんらかのロックを使用する必要があります。ロックを行うと、シングルスレッド用のアプリケーションの場合でもいくらかオーバーヘッドが起こります。ロックを使用するか、あるいは、iostream オブジェクトをスレッドの非公開オブジェクトとするかは、アプリケーションで採用しているスレッドモデルによります。
各スレッドが独立の場合と、複数スレッドが共同作業を行う場合
それぞれの独立したスレッドが自分自身の iostream オブジェクトを使用してデータの入出力を行う場合は、各 iostream オブジェクトをそれぞれのスレッドの非公開 オブジェクトとし、ロックを使用する必要はありません。
複数スレッドで共同作業を行う場合 (すなわち同一の iostream オブジェクトを共有しなければならない場合) は、共有オブジェクトへのアクセスは同期をとって行う必要があり、一連の操作を不可分命令的に行うためのなんらかのロックを使用する必要があります。
iostream ライブラリでは、iostream オブジェクトに対する一連の操作をロックするための stream_locker クラスが提供されています。したがって、iostream のロックとロック解除を動的に設定することによるオーバーヘッドを最小にすることができます。
stream_locker クラスのオブジェクトを使用すると、ストリームオブジェクトに対する一連の操作を不可分命令的に実行することができます。たとえば、次の例では、ファイル内の位置を指定して、そこからデータを 1 ブロック読み込みます。
#include <fstream.h>
#include <rlocks.h>
void lock_example (fstream& fs)
{
const int len = 128;
char buf[len];
int offset = 48;
stream_locker s_lock(fs, stream_locker::lock_now);
. . . . .// ファイルをオープン
fs.seekg(offset, ios::beg);
fs.read(buf, len);
}
この例では、stream_locker オブジェクトのコンストラクタが相互排他制御域の開始を定義します。相互排他制御域では、一度に 1 つのスレッドしか実行されません。また、関数から戻った後で呼び出されるデストラクタでは、相互排他制御域の終了を定義します。したがって、stream_locker オブジェクトにより、ファイル内の特定の位置のシークと、ファイルからのデータの読み込みとが不可分命令的に実行されます。スレッド A がファイルからデータを読み込む前に、スレッド B がファイル内の位置を変えてしまうことができなくなるためです。
stream_locker オブジェクトのもう 1 つの使用方法として、相互排他制御域を明示的に定義する方法があります。次の例では、入出力操作とそれに続くエラー検査を不可分命令的に実行するため、stream_locker オブジェクトのメンバー関数 lock と unlock を呼び出します。
{
...
stream_locker file_lck(openfile_stream,
stream_locker::lock_defer);
....
file_lck.lock(); // openfile_stream をロック
openfile_stream << "Value: " << int_value << "¥n";
if(!openfile_stream) {
file_error("Output of value failed¥n");
return;
}
file_lck.unlock(); // openfile_stream のロックを解除
}
stream_locker についての詳細は、stream_locker(3) のマニュアルページを参照してください。