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

スレッド固有データキーの取得

pthread_getspecific(3THR)

pthread_getspecific(3THR) は、key についての呼び出しスレッドの割り当てを取得し、それを value が指している記憶場所に格納します。


プロトタイプ:
void * pthread_getspecific(pthread_key_t key);

#include <pthread.h>

pthread_key_t key;
void *value;

/*  前に作成されたキー */
value = pthread_getspecific(key); 

戻り値

エラーは戻されません。

スレッド固有データの広域性と局所性の例

例 2-2 は、あるマルチスレッドプログラムからの抜粋です。このコードは任意の数のスレッドによって実行されますが、2 つの広域変数 errnomywindow は、実際には各スレッドにとって局所的な変数として参照されます。


例 2-2 スレッド固有データの広域性と局所性


body() {
    ...

    while (write(fd, buffer, size) == -1) {
        if (errno != EINTR) {
            fprintf(mywindow, "%s¥n", strerror(errno));
            exit(1);
        }
    }

    ...

}

errno を参照すれば、そのスレッドが呼び出したルーチンから戻されたシステムエラーコードがわかります。他のスレッドが呼び出したシステムコールではありません。つまり、スレッドによる errno の参照は、スレッドごとに異なる記憶領域を参照します。

変数 mywindow は、それを参照するスレッドの専用のウィンドウに接続される stdio ストリームを参照するための変数です。errno と同様、スレッドによる mywindow の参照は、スレッドごとに異なる記憶領域、つまり異なるウィンドウを参照します。唯一の違いは、errno はスレッドライブラリが面倒を見てくれるのに対し、mywindow はプログラマが自分で管理しなければならないことです。

例 2-3 は、mywindow の参照がどのように働くかを示しています。プリプロセッサは、mywindow の参照を _mywindow() 手続きの呼び出しに変換します。

このルーチンは pthread_getspecific() を呼び出し、広域変数 mywindow_key (これは実際の広域変数) と出力用のパラメタ win を渡します。win には、そのスレッドのウィンドウの識別子が戻されます。


例 2-3 広域参照から局所参照への変換


thread_key_t mywin_key;

FILE *_mywindow(void) {
    FILE *win;

    pthread_getspecific(mywin_key, &win);
    return(win);
}

#define mywindow _mywindow()

void routine_uses_win( FILE *win) {
    ...
}

void thread_start(...) {
    ...
    make_mywin();
    ...
    routine_uses_win( mywindow )
    ...
}

変数 mywin_key は、スレッド毎に実体を持つことができる変数のまとまりを識別します。つまり、これらの変数はスレッド固有データです。各スレッドは make_mywin() を呼び出し、そこで自分専用のウィンドウを初期化し、参照用に mywindow の自分専用のインスタンスを配置します。

make_mywin() を呼び出したスレッドは、mywindow を安全に参照できるようになり、さらに _mywindow() の実行後は、自分専用のウィンドウを参照できるようになります。結果的に、mywindow の参照は、そのスレッドの専用のデータの直接の参照であるかのように見えます。

例 2-4 は、以上の処理を示しています。


例 2-4 スレッド固有データの初期化


void make_mywindow(void) {
    FILE **win;
    static pthread_once_t mykeycreated = PTHREAD_ONCE_INIT;

    pthread_once(&mykeycreated, mykeycreate);

    win = malloc(sizeof(*win));
    create_window(win, ...);

    pthread_setspecific(mywindow_key, win);
}

void mykeycreate(void) {
    pthread_keycreate(&mywindow_key, free_key);
}

void free_key(void *win) {
    free(win);
}

まず最初に、mywin_key キーに一意的な値を取得します。これはスレッド固有データのクラスを識別するために使用するキーです。具体的には、make_mywin() を呼び出す最初のスレッドが pthread_key_create() を呼び出します。その結果、この関数の第 1 引数に一意なキーが割り当てられます。第 2 引数はデストラクタ関数で、このスレッド固有データ項目のスレッド専用インスタンスをスレッドの終了時に解放するためのものです。

次に、呼び出し側の、このスレッド固有データ項目のインスタンスのために記憶領域を確保します。記憶領域を確保した後、create_window() ルーチンが呼び出されます。このルーチンでは、スレッドのためにウィンドウを設定し、そのウィンドウを参照するために win の指す記憶領域を設定します。最後に pthread_setspecific() が呼び出され、win 内の値 (つまり、ウィンドウの参照が格納されている記憶領域の位置) とキーとが結び付けられます。

その後、スレッドは pthread_getspecific() を呼び出して上記の広域キーを渡します。その結果、スレッドが pthread_setspecific() を呼び出して、このキーに関連付けた値を取得できます。

スレッドが終了するときは、pthread_key_create() で設定したデストラクタ関数が呼び出されます。各デストラクタ関数は、そのスレッドが pthread_setspecific() でキーに値を設定している場合だけ呼び出されます。