WOODWOOD77777のHP

Pthreadプログラミング(2)
DETACHED属性の利点と問題点の解決

2006/02/03 誤字訂正
2006/01/24 タイトル変更。説明の一部更新。
2006/01/19 更新
2006/01/18 アップロード

前のページ:Pthreadプログラミング(1) 新しいスレッドを作成する


Contents

サンプルプログラム(pthread_detached01.c) ・・・問題あり
サンプルプログラム(pthread_detached02.c) ・・・問題解決

サンプルプログラム(pthread_detached01.c)

前回のページでは、新しく作成したスレッドを作成する際にデフォルトの属性を与え、pthread_join()で待機しました。
今回のページでは、スレッドを作成する際に属性に「PTHREAD_CREATE_DETACHED」を与えます。
この属性を与えると、スレッドが終了するのを待たずに、main()スレッドが終了します。(pthread_joinable()で待機しない)
(detachstate属性には、PTHREAD_CREATE_DETACHEDPTHREAD_CREATE_JOINABLE(デフォルト)の二つがあります。)
(実はマルチスレッドプログラミングではmain()も一つのスレッドなのです。)
しかし、以下のプログラムでは問題があります。


#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void *thread_func(void *param)
{
  int pid;
  int i;

  pthread_t thread_id;
  thread_id = pthread_self();
  fprintf(stderr , "thread_func called\n");
  fprintf(stderr , "  thread ID = %ld\n" , thread_id);
  pid = getpid();
  fprintf(stderr , "  2:pid=%d\n" , pid);
  for(i = 0 ; i < 6 ; i++)
  {
        sleep(1);
        fprintf(stderr , "thread_func() : count = %d\n" , i);
  }
  

  return NULL;
}


int main(int argc, char *argv[])
{
  pthread_t thread;
  pthread_attr_t thread_attr;
  int pid;
  int i;

  fprintf(stderr , "---Program start---\n");
  pid = getpid();
  fprintf(stderr , "1:pid = %d\n" , pid);
  
  pthread_attr_init(&thread_attr);
  pthread_attr_setdetachstate(&thread_attr , PTHREAD_CREATE_DETACHED);  /*この属性をつけてスレッドを作成すると、main()スレッドはfunc_thread()スレッドの終了を待たない*/
  if(pthread_create(&thread , &thread_attr , thread_func , NULL) !=0)
        perror("pthread_create()");
  pthread_attr_destroy(&thread_attr);
  fprintf(stderr , "Next line of pthread_create() called. thread ID=%ld\n" , thread);
  for(i = 0 ; i < 3 ; i++)
  {
        sleep(1);
        fprintf(stderr , "main() : count=%d\n" , i);
  }
  fprintf(stderr , "---Program end---\n");
  return EXIT_SUCCESS;
}



実行結果
---Program start---
1:pid = 2170
thread_func called
  thread ID = 1026
  2:pid=2172
Next line of pthread_create() called. thread ID=1026
main() : count=0
thread_func() : count = 0
main() : count=1
thread_func() : count = 1
main() : count=2
---Program end---
thread_func() : count = 2
------------

このプログラムでは、main()スレッドが終了しても、
thread_func() : count = 5
と表示されるまで、thread_func()スレッドが生きていることを期待していました。
しかし、実際は、thread_func()スレッドが動いている間に、プログラムは終了してしまいます。
これでは、重要な処理をPTHREAD_CREATE_DETACHED属性を割り当てたスレッドに任せられません。

これを解決するには、main()スレッド内で、pthread_exit()関数を呼び出します。
以下のサンプルプログラムはpthread_detached01.cに一行追加しただけです。
===========================================================================
サンプルプログラム(pthread_detached02.c)

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>

void *thread_func(void *param)
{
  int pid;
  int i;

  pthread_t thread_id;
  thread_id = pthread_self();
  fprintf(stderr , "thread_func called\n");
  fprintf(stderr , "  thread ID = %ld\n" , thread_id);
  pid = getpid();
  fprintf(stderr , "  2:pid=%d\n" , pid);
  for(i = 0 ; i < 6 ; i++)
  {
        sleep(1);
        fprintf(stderr , "thread_func() : count = %d\n" , i);
  }
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t thread;
  pthread_attr_t thread_attr;
  int pid;
  int i;

  fprintf(stderr , "---Program start---\n");
  pid = getpid();
  fprintf(stderr , "1:pid = %d\n" , pid);
  
  pthread_attr_init(&thread_attr);
  pthread_attr_setdetachstate(&thread_attr , PTHREAD_CREATE_DETACHED);
  if(pthread_create(&thread , &thread_attr , thread_func , NULL) !=0)
        perror("pthread_create()");
  pthread_attr_destroy(&thread_attr);
  fprintf(stderr , "Next line of pthread_create() called. thread ID=%ld\n" , thread);
  for(i = 0 ; i < 3 ; i++)
  {
        sleep(1);
        fprintf(stderr , "main() : count=%d\n" , i);
  }
  fprintf(stderr , "---Program end---\n");
  pthread_exit(NULL); //この行を加える
  return EXIT_SUCCESS;
}


実行結果
---Program start---
1:pid = 2413
thread_func called
  thread ID = 1026
  2:pid=2415
Next line of pthread_create() called. thread ID=1026
main() : count=0
thread_func() : count = 0
main() : count=1
thread_func() : count = 1
main() : count=2
---Program end---
thread_func() : count = 2
thread_func() : count = 3
thread_func() : count = 4
thread_func() : count = 5

------------
thread_func()スレッドが終了するまで、プログラムが終了しないのを確認できました。
main()スレッド内で、pthread_exit()関数を呼び出すと、main()スレッドが終了しても、他のスレッドが全て終了するまでプログラムは終了しません。

これでは、前回のpthread_join()で待機するのと同じではないかと思われるかもしれませんが、pthread_join()は一つのスレッドしか待機することはできません。
PTHREAD_CREATE_DETACHED属性でスレッドを作成し、pthread_exit()関数を呼び出す方式だと、スレッドが複数あっても、スレッドは最後まで、仕事をしてくれます。

スレッドが一つと限った場合は、pthread_join()で、合流して、複数のスレッドを作成するときは、PTHREAD_CREATE_DETACHED属性でスレッドを作成し、main()スレッドでpthread_exit()で終了するといった感じでいいんじゃないでしょうか?

しかし、スレッドが新たに加わる可能性があることを考えると、個人的には、今回のpthread_exit()を利用したプログラムの方が安全なような気がしますが・・・どうでしょうか?。