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

広域変数の考慮

現状では大半のコード、特に C プログラムから呼び出されるライブラリルーチンは、シングルスレッドアプリケーション向けに設計されています。シングルスレッド用のコードでは、次のように仮定していました。

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

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


例 10-1 広域変数と errno


extern int errno;
...
if (write(file_desc, buffer, size) == -1) {
    /* システムコールが失敗 */
    fprintf(stderr, "something went wrong, "
        "error code = %d¥n", errno);
    exit(1);
}
...

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

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

スレッドでは、この問題を解決するために、スレッド固有データという新しい記憶クラスを導入しています。このスレッド固有データは、スレッド内の任意の手続きからアクセスできるという点で広域変数と似ています。ただし、これはそのスレッドに専用の領域です。つまり、2 つのスレッドが同じ名前のスレッド固有データをアクセスしても、それぞれ異なる変数をアクセスしていることになります。

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