pthread_join和pthread_detach详解

来源:互联网 发布:宣传画册设计软件 编辑:程序博客网 时间:2024/06/06 11:13
在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。

    默认情况下,线程被创建成可结合的。为了避免存储器泄漏,每个可结合线程都应该要么被显示地回收,即调用pthread_join;要么通过调用pthread_detach函数被分离。

[cpp]
int pthread_join(pthread_t tid, void**thread_return); 
                                 若成功则返回0,若出错则为非零。 

int pthread_join(pthread_t tid, void**thread_return);
                                 若成功则返回0,若出错则为非零。    线程通过调用pthread_join函数等待其他线程终止。pthread_join函数分阻塞,直到线程tid终止,将线程例程返回的(void*)指针赋值为thread_return指向的位置,然后回收已终止线程占用的所有存储器资源。[cpp] view plaincopyprint?int pthread_detach(pthread_t tid); 
                                 若成功则返回0,若出错则为非零。 

int pthread_detach(pthread_t tid);
                                 若成功则返回0,若出错则为非零。

    pthread_detach用于分离可结合线程tid。线程能够通过以pthread_self()为参数的pthread_detach调用来分离它们自己。
    如果一个可结合线程结束运行但没有被join,则它的状态类似于进程中的Zombie Process,即还有一部分资源没有被回收,所以创建线程者应该调用pthread_join来等待线程运行结束,并可得到线程的退出代码,回收其资源。

    由于调用pthread_join后,如果该线程没有运行结束,调用者会被阻塞,在有些情况下我们并不希望如此。例如,在Web服务器中当主线程为每个新来的连接请求创建一个子线程进行处理的时候,主线程并不希望因为调用pthread_join而阻塞(因为还要继续处理之后到来的连接请求),这时可以在子线程中加入代码

    pthread_detach(pthread_self())

或者父线程调用

    pthread_detach(thread_id)(非阻塞,可立即返回)

这将该子线程的状态设置为分离的(detached),如此一来,该线程运行结束后会自动释放所有资源。


通过linux的pthread库, 相信大家对创建/销毁线程肯定很熟悉, 不过对pthread_join是否知道的更多呢?
先编写一个常规的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <pthread.h>
 
#include <stdio.h>
#include <string.h>
 
void *thread_rountine(void * /*args*/) {
  printf("thread %lu work\n", pthread_self());
}
 
int main()
{
 
  pthread_t tid;
 
  // *) 创建线程
  pthread_create(&tid, NULL, thread_rountine, NULL);   
 
  // *) 等待线程结束
  pthread_join(tid, NULL);
 
  return0;
 
}

  评注: 这是简单的关于线程创建, 以及通过pthread_join阻塞等待子线程退出的例子

  pthread_join是否真的只是为了执行顺序, 等待子线程退出的一种机制? 难道就没有其他作用了吗?
  答案是肯定的, 我们先来写个程序, 做下对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <sys/time.h>
#include <sys/resource.h>
 
#include <pthread.h>
 
#include <stdio.h>
#include <string.h>
 
void *thread_rountine(void * /*args*/) {
  //pthread_detach(pthread_self());  ==> (2)
}
 
int main()
{
  
  rlimit rl;
  getrlimit(RLIMIT_NPROC, &rl);
 
  for( int i = 0; i < rl.rlim_cur; i++ ) {
    pthread_t tid;
    intret = pthread_create(&tid, NULL, thread_rountine, NULL);
    if( ret != 0 ) {
      printf("error_msg => %s\n",strerror(ret));
      break;
    }
    // pthread_join(tid, NULL);  ==> (1)
  }
 
  return0;
 
}

  评注: 这边我们去掉了(1)pthread_join, (2) pthread_detach
  最终的程序输出结果为:

1
error_msg => Resource temporarily unavailable

  我们可以大胆地猜想进程的线程数有个限制, 那我们的程序究竟在那个环节出错了? 疏忽什么导致线程资源没被完全收回?

  和Java开发多线程程序相比, 觉得搞java的人实在太幸福了.

  在回到原问题, 如果我们添加(1)pthread_join, 或者(2)pthread_detach, 问题解决了.
  我们查下man, 看看"专家"如何解说pthread_join

1
Failure to join with athread that is joinable (i.e., one that is not detached), produces a"zombie thread". Avoid doingthis, since each zombiethread consumes somesystem resources, and when enough zombie threads have accumulated, it will no longer be possible to createnew threads (or processes).

  评注: 如果没有对joinable的线程, 作pthread_join, 会导致线程成为"zombie thread", 依旧会占据这个资源. 这也就不难理解了,为何明明都是短任务线程, 没有加pthread_join, 主线程就不能再创建线程了. pthread_join其实作了部分线程销毁的工作. 另一方面, 子线程能通过pthread_detach函数, 在线程自身退出时做好清理工作.