7. スレッド固有データ(Thread Specific Data:TSD)
スレッドはあくまでプロセスの一部である事は既に述べました。スレッドはプロセスとさまざまな情報が共有されています。1章でも述べましたが、スレッドが独立で管理できる情報、つまりスレッド固有なデータは下記のみです。
- スタック
- スレッドID (
pthread_t
) - シグナルマスク (
pthread_sigmask(3)
) ※ signalについて 参照 - 代替シグナルスタック (
sigaltstack(2)
) ※ signalについて 参照 - errno変数
- スケジューリングのポリシーと優先度 ※ 別章参照
上記以外の情報、例えばプロセスIDなどの各種IDやグローバルなデータはプロセス共有となっています。
しかし、スレッドが使用できるメモリがスタックだけでは都合が悪い時もあります。例えばスレッド内から呼ばれる関数でエラー情報をグローバルな変数にセットする場合、その変数はスレッド固有でないと、エラー情報が上書かれてしまうなどの弊害が発生します。
上記以外でスレッド固有なデータを取得するには別途方法があります。特にグローバルな変数に対して完全にスレッド固有な、スレッドにとってプライベートな領域を確保する方法について述べていきます。
7.1 リファレンス
主にスレッド固有データの生成にかかわる関数は下記の通りです。より詳細についてはman
コマンド、もしくはココを参照してください。
- int pthread_key_create( pthread_key_t * key, void ( *destr_function )( void * ));
pthread_key_t
:スレッド固有データキー。このキーに対してスレッド固有情報を取得します。1回だけpthread_key_create
を実行する必要があります。通常、pthread_once
を利用します。destr_function
:いわゆるデストラクタです。使用しない場合はNULLでも良いです。主な使用法は8.3で説明します。- int pthread_key_delete( pthread_key_t key );
- int pthread_setspecific( pthread_key_t key, const void * pointer );
- void * pthread_getspecific( pthread_key_t key );
pthread_setspecific
を使って登録します。7.2 プログラム
まず下記のサンプルを実行してください。
guest $ ./tsd_once pthread_getspecific is NULL!! [thread 1] set tsd value 0x81be560 [thread 1] starting... [thread 2] set tsd value 0x81be560 [thread 2] starting... [thread 3] set tsd value 0x81be560 [thread 3] starting... thread 3 done... thread 3 done... thread 3 done... guest $
実行すると3つのスレッドの情報が出力されますが、同じアドレスを参照している事が分かります。参照しているアドレスはグローバルな変数で、各スレッドが書き込みや参照をしています。最後にスレッドが終了する際に出力される情報は、最後に動いたスレッドによって上書かれている事が分かります。
次のサンプルを試してみてください。
guest $ ./tsd_once_2nd initializing key pthread_getspecific is NULL!! [thread 1] set tsd value 0x9550560 [thread 1] starting... pthread_getspecific is NULL!! [thread 2] set tsd value 0x9550570 [thread 2] starting... pthread_getspecific is NULL!! [thread 3] set tsd value 0x9550580 [thread 3] starting... thread 1 done... thread 2 done... thread 3 done... guest $
同様に実行すると、先ほどとは違って上書きされておらず固有のデータとして扱われているのが分かります。参照しているアドレスは先ほどと同じグローバルな変数で、各スレッドが書き込みや参照をしています。
もう1つ、次のサンプルを試してみてください。
guest $ ./tsd_once_3rd pthread_getspecific is NULL!! [thread 1] set tsd value 0x8969578 [thread 1] starting... pthread_getspecific is NULL!! [thread 2] set tsd value 0x8969588 [thread 2] starting... pthread_getspecific is NULL!! [thread 3] set tsd value 0x8969598 [thread 3] starting... thread 1 done... thread 2 done... thread 3 done... guest $
これも同様に固有のデータとして扱われているのが分かります。このサンプルはtsd_once.cとほとんど同じで、一点だけ__thread
キーワードが付加されいるだけです(動かない場合は環境が新しくないという事になります)。
guest $ diff tsd_once.c tsd_once_3rd.c 2,3c2,3 < gcc tsd_once.c -o tsd_once -W -Wall -g -lpthread < */ --- > gcc tsd_once_3rd.c -o tsd_once_3rd -W -Wall -g -lpthread > */ 13c13 < tsd_t * value = 0; --- > __thread tsd_t * value = 0; guest $
__thread
キーワードについては後述します。