2007-02-05T22:50+09:00 matsu
スレッドのデタッチと合流について調べてみた.
スレッドの終了とjoin
デタッチ
サンプル
スレッドの終了とjoin
スレッドは終了してもリソース(スレッドディスクリプタとスタック)が保持される. このリソースは,他のスレッドが開放するまで保持され続ける. この開放処理が
include
int pthread_join(pthread_t th,
void **thread_return);
である. 返り値は0なら成功,それ以外の場合にはエラーコードが格納されている. 引数は以下.
th
スレッド識別子. join対象のスレッドをpthread_createした際に取得したもの.
thread_return
join対象のスレッドのイニシャル関数の返り値(void *)の格納領域のポインタ.
この関数を呼び出した時点で,対象のスレッドが終了していないければ,そのスレッドが終了するのを待ってからリソースは開放される. プロセスでforkした後のwaitをイメージすれば近いだろうか. だから,この関数はその名の通り,スレッドの合流と解した方が,その挙動を解しやすいかもしれない.
デタッチ
スレッドが終了したら,別のスレッドでその終了したスレッドのリソースを開放しなければならない. だが,プログラムによっては,このリソース開放処理を記述する適切な箇所が見当たらなかったり,プログラム構造に制約を課してしまう場合がある. このような場合には,該当スレッドにてpthread_createする際の属性にてデタッチ属性を指定するか(※),
include
int pthread_detach(pthread_t th);
を呼び出す. 返り値は0なら成功,0以外ならエラーコードが設定されている. 引数にはやはり,デタッチ対象のスレッドの識別子を指定する. スレッドをデタッチすることによって,対象のスレッドは終了時にリソースが開放されることを保証される. ただし,合流可能状態でなくなり,pthread_joinできなくなる. デタッチされたスレッドに対しpthread_joinを実行すると,エラーが返される. また,pthread_joinにて合流待ちされているスレッドに対してpthread_detachを呼び出すと,何も処理されず0が返される.
※ 別頁にて記述.
サンプル
pthread_join,pthread_detachを実行するサンプルを以下に示す.
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
/*
* スレッドパラメータ格納用
*/
typedef struct {
char printVal;
int interval;
} MY_THREAD_ARG;
/*
* スレッドイニシャル関数
*/
void *myThread(void *arg)
{
MY_THREAD_ARG *my_thread_arg =(MY_THREAD_ARG*)arg;
int i = 0;
// スレッド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");
}
for(i = 0; i < 5; i++) {
fprintf(stderr,"%c", my_thread_arg->printVal);
sleep(my_thread_arg->interval);
}
return arg;
}
int main(int argc,char *argv[])
{
int status;
void *thread_return;
// スレッドa用のパラメータ
pthread_t thread_a;
MY_THREAD_ARG thread_a_arg;
thread_a_arg.printVal = 'a';
thread_a_arg.interval = 1;
// スレッドb用のパラメータ
pthread_t thread_b;
MY_THREAD_ARG thread_b_arg;
thread_b_arg.printVal = 'b';
thread_b_arg.interval = 2;
// スレッドaを生成
status=pthread_create(&thread_a, NULL, myThread, &thread_a_arg);
if(status!=0){
fprintf(stderr, "\n");
fprintf(stderr,"failed to create thread_a");
fprintf(stderr, "\n");
exit(1);
}
// スレッドbを生成
status=pthread_create(&thread_b, NULL, myThread, &thread_b_arg);
if(status!=0){
fprintf(stderr, "\n");
fprintf(stderr,"failed to create thread_a");
fprintf(stderr, "\n");
exit(1);
}
// スレッド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);
}
// スレッド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);
}
fprintf(stderr, "\n");
return 0;
}
このサンプルでは,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
pthread_t pthread_self(void);
を呼び出している. さて,スレッドbはpthread_createされた後即座に(メインスレッドにてpthread_joinされる前に)pthread_detachをしてしまうので,このpthread_detach呼び出しによって,合流可能状態ではなくなる. メインスレッドは後からスレッドbに対しpthread_joinするが,スレッドbは合流可能状態ではないので失敗する.
// スレッド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に失敗したことがわかる.