「スレッド安全」は、データの競合が回避されていることを示します。データの競合は、複数のスレッドがデータをアクセスして変更するときに、その順番によってデータの値が正しくなったり正しくなくなったりするときに発生します。
スレッド間でデータを共有する必要がない場合は、スレッドごとに専用のコピーを与えますが、共有する必要がある場合には、明示的に同期をとることによってプログラムが確定的な動きをするように制御する必要があります。
手続きが「スレッド安全」とは、その手続きが複数のスレッドによって同時に実行されても論理的な正しさが失われないことです。実際は、安全性は次の 3 段階で区別されます。
「安全ではない」
「スレッド安全」— 直列化
「スレッド安全」— MT-安全
「スレッド安全ではない」手続きであっても、mutex をロックする命令と解除する命令で囲めば、その処理は直列化され「スレッド安全」になります。例 7–1 は fputs() を簡略化したもので、最初のルーチンは「スレッド安全ではない」例です。
2 番目のルーチンは直列化した例です。ここでは、1 つの mutex で手続きを並行実行させないようにしています。この 1 つの mutex は、通常必要とされる同期よりも強い同期となります。2 つのスレッドが fputs() を使って異なるファイルに出力するときは、一方がもう一方を待つ必要はありません。これらのスレッドは、出力ファイルを共有している場合にのみ同期を必要とします。
最後のルーチンは、「MT-安全」の例です。このバージョンでは、ファイルごとに mutex をロックしているので、2 つのスレッドが異なるファイルに同時に出力できます。つまり、ルーチンが「MT-安全」であるとは「スレッド安全」で、しかもそのルーチンの実行が性能に悪影響を及ぼさないということになります。
/* not thread-safe */ fputs(const char *s, FILE *stream) { char *p; for (p=s; *p; p++) putc((int)*p, stream); } /* serializable */ fputs(const char *s, FILE *stream) { static mutex_t mut; char *p; mutex_lock(&m); for (p=s; *p; p++) putc((int)*p, stream); mutex_unlock(&m); } /* MT-Safe */ mutex_t m[NFILE]; fputs(const char *s, FILE *stream) { char *p; mutex_lock(&m[fileno(stream)]); for (p=s; *p; p++) putc((int)*p, stream); mutex_unlock(&m[fileno(stream)]0; }