マルチスレッドのプログラミング

大域変数の考慮

従来のほとんどのコードは、シングルスレッドプログラム用に設計されています。このコード設計は、特に C プログラムから呼び出されるライブラリルーチンによく見られます。シングルスレッド用のコードでは、次のように仮定していました。

次に、上記の仮定が原因で生じるマルチスレッドプログラム上の問題とその対処例を示します。

従来のシングルスレッドの C および UNIX では、システムコールで検出されたエラーの処理について一定の決まりがあります。システムコールは、関数値として任意の値を戻すことができます。たとえば、write() は、転送されたバイト数を返します。ただし、値 -1 は、エラーが生じたことを示すために予約されています。つまり、システムコールから -1 が戻された場合は、システムコールが失敗したことを意味します。


例 9–1 大域変数と errno

extern int errno;
...
if (write(file_desc, buffer, size) == -1) {
    /* the system call failed */
    fprintf(stderr, “something went wrong, “
        “error code = %d\n”, errno);
    exit(1);
}
...

通常の戻り値と混同されがちですが、実際のエラーコードは返されず、大域変数 errno に格納されます。システムコールが失敗した場合は、errno を調べればエラーの内容を知ることができます。

ここで、マルチスレッド環境において 2 つのスレッドが同時に失敗し、異なるエラーが発生したと仮定します。このとき、両方のスレッドがエラーコードは errno に入っていると期待しても、1 つの errno に両方の値を保持することは不可能です。このように、マルチスレッドプログラムでは、大域変数による方法は使用できません。

スレッドは、この問題を、新しい概念の記憶領域クラスであるスレッド固有データを使用することで解決します。この記憶領域は大域記憶領域と似ています。スレッド固有データは、スレッドの実行が可能な任意の手続きからアクセスできます。ただし、スレッド固有データは特定のスレッド専用です。2 つのスレッドが同じ名前のスレッド固有データを参照した場合、これらのスレッドはそれぞれ異なる記憶領域を参照していることになります。

したがって、スレッドを使用しているときは、スレッドごとに errno の専用のコピーが与えられるので、errno の参照がスレッドに固有なものとなります。この実装では、errno をマクロにして関数呼び出しを拡張することで、スレッド固有のものとして errno を参照できるようになっています。