11
2月
2007

スレッドのデタッチとjoin

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に失敗したことがわかる.

You may also like...