スレッドのデタッチとjoin
スレッドのデタッチと合流について調べてみた.
スレッドは終了してもリソース(スレッドディスクリプタとスタック)が保持される.
このリソースは,他のスレッドが開放するまで保持され続ける.
この開放処理が
#include <pthread.h>
int pthread_join(pthread_t th,
void **thread_return);
である.
返り値は0なら成功,それ以外の場合にはエラーコードが格納されている.
引数は以下.
- th
- スレッド識別子. join対象のスレッドをpthread_createした際に取得したもの.
- thread_return
- join対象のスレッドのイニシャル関数の返り値(void *)の格納領域のポインタ.
スレッドが終了したら,別のスレッドでその終了したスレッドのリソースを開放しなければならない.
だが,プログラムによっては,このリソース開放処理を記述する適切な箇所が見当たらなかったり,プログラム構造に制約を課してしまう場合がある.
このような場合には,該当スレッドにてpthread_createする際の属性にてデタッチ属性を指定するか(※),
#include <pthread.h> int pthread_detach(pthread_t th);を呼び出す. 返り値は0なら成功,0以外ならエラーコードが設定されている. 引数にはやはり,デタッチ対象のスレッドの識別子を指定する. スレッドをデタッチすることによって,対象のスレッドは終了時にリソースが開放されることを保証される. ただし,合流可能状態でなくなり,pthread_joinできなくなる. デタッチされたスレッドに対しpthread_joinを実行すると,エラーが返される. また,pthread_joinにて合流待ちされているスレッドに対してpthread_detachを呼び出すと,何も処理されず0が返される.
※ 別頁にて記述.
pthread_join,pthread_detachを実行するサンプルを以下に示す.
このサンプルでは,main関数にて二つのスレッドa,bを生成し,どちらも関数myThreadを実行する.
myThreadでは,スレッドaは2秒sleepし,その間にmainスレッドはスレッドaに対するpthread_joinにまで到達する.
実行結果は以下.
// スレッドaが終了するのを待つ.
status = pthread_join(thread_a, &thread_return);
if (status != 0) {
fprintf(stderr, "\n");
fprintf(stderr,"failed to join thread_a");
fprintf(stderr, "\n");
exit(1);
} else {
// スレッドのイニシャル関数の返り値をチェック.
assert(&thread_a_arg == thread_return);
}
その後スレッドaはpthread_detachしようとするが,すでにmainスレッドにて合流待ちされているので,デタッチされずにpthread_detach呼び出しは完了してしまう(下図破線内).
// スレッドaのみ2秒sleep
if (my_thread_arg->printVal == 'a') {
sleep(2);
}
// 自スレッド識別情報の取得
pthread_t self_thread = pthread_self();
// デタッチ
int status = pthread_detach(self_thread);
if (status != 0) {
fprintf(stderr, "failed to detatch\n");
}
なお,スレッドa内では,自分自信のスレッド識別子を取得するために,
#includeを呼び出している. さて,スレッドbはpthread_createされた後即座に(メインスレッドにてpthread_joinされる前に)pthread_detachをしてしまうので,このpthread_detach呼び出しによって,合流可能状態ではなくなる. メインスレッドは後からスレッドbに対しpthread_joinするが,スレッドbは合流可能状態ではないので失敗する.pthread_t pthread_self(void);
// スレッドbはデタッチしているので,joinに失敗する.
status = pthread_join(thread_b, &thread_return);
if (status != 0) {
fprintf(stderr, "\nexpected result\n");
} else {
fprintf(stderr, "\n");
fprintf(stderr,"successed to join thread_a");
fprintf(stderr, "\n");
exit(1);
}
以上のシーケンスを以下に示す.
$ ./join_thread bababaaba expected resultよくわからないが,とにかく"expected result"というふうに,スレッドbのjoinに失敗したことがわかる.

