首页
登录 | 注册

POSIX线程状态:Joinable和Detached

        原文链接: http://dongliang202.blog.163.com/blog/static/79083735201062401032995/

一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。

 

void pthread_exit(void *retval) 
int pthread_join(pthread_t th, void **thread_return)
int pthread_detach(pthread_t th)

 

pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。

如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。

一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。

 

 

 首先看一下两个函数的定义:

int pthread_join(
             pthread_t tid ,
             void **status
             );
// 参数tid 是希望等待的线程的线程号,status 是指向线程返回值的指针,线程的返回值就是pthread_exit 中的value_ptr 参数,或者是return语句中的返回值。该函数可用于线程间的同步


int pthread_detach( pthread_t pid );

//参数tid 是希望等待的线程的线程号, 把指定的线程转变为脱离状态
一个线程或者是可汇合的(joinable,缺省值),或者是脱离的(detached)。当一个可汇合的线程终止时,它的线程ID和退出状态将留到另一个线程对它调用pthread_join。脱离线程却象守护进程:当它们终止的时,所有相关资源都被释放,我们不能等待它们终止。如果一个线程需要知道另一个线程什么时候终止,那就最好好吃第二个线程的可汇合状态。

    下面 通过例子说明:

#include
#include
#include
#include

#define THREAD_NUMBER 2
int retval_hello1= 1, retval_hello2 = 2;

void* hello1(void *arg) 
{    
   char *hello_str = (char *)arg;
   sleep(2);
   printf("%s/n", hello_str);
   pthread_exit(&retval_hello1);
}

void* hello2(void *arg)
{
   char *hello_str = (char *)arg;
   sleep(1);
   printf("%s/n", hello_str);
   pthread_exit(&retval_hello2);
}

int main(int argc, char *argv[])
{
   int i;
   int ret_val;
   int *retval_hello[2];
   
   pthread_t pt[THREAD_NUMBER];
   const char *arg[THREAD_NUMBER];
   arg[0] = "hello world from thread1";
   arg[1] = "hello world from thread2";
   
   printf("Begin to create threads.../n");
   ret_val = pthread_create(&pt[0], NULL, hello1, (void *)arg[0]);
   if (ret_val != 0 ) {
      printf("pthread_create error!/n");
      exit(1);
   }

   ret_val = pthread_create(&pt[1], NULL, hello2, (void *)arg[1]);
   if (ret_val != 0 ) {
      printf("pthread_create error!/n");
      exit(1);
   }
   
   printf("Begin to wait for threads.../n");   
   for(i = 0; i < THREAD_NUMBER; i++) {
       ret_val = pthread_join(pt[i], (void **)&retval_hello[i]);
       if (ret_val != 0) {
          printf("pthread_join error!/n");
      exit(1);
       } else {
          printf("return value is %d/n", *retval_hello[i]);
       }
   }
   printf("Now, the main thread returns./n");
   return 0;
}

执行结果为:

Begin to create threads...
Begin to wait for threads...
hello world from thread2
hello world from thread1
return value is 1
return value is 2
Now, the main thread returns.

线程1,2的执行顺序可以通过sleep来调节,但是主线程必须在子线程完成之后才能执行,即打印”Now, the main thread returns.“。此外,因为调用pthread_join()的顺序,必定是线程1先执行“return value is xx”,不管线程2是否先执行完。

下面修改pthread_join为pthread_detach(),代码为

#include
#include
#include
#include

#define THREAD_NUMBER 2
int retval_hello1= 1, retval_hello2 = 2;

void* hello1(void *arg) 
{    
   char *hello_str = (char *)arg;
   sleep(2);
   printf("%s/n", hello_str);
   pthread_exit(&retval_hello1);
}

void* hello2(void *arg)
{
   char *hello_str = (char *)arg;
   sleep(1);
   printf("%s/n", hello_str);
   pthread_exit(&retval_hello2);
}

int main(int argc, char *argv[])
{
   int i;
   int ret_val;
   int *retval_hello[2];
   
   pthread_t pt[THREAD_NUMBER];
   const char *arg[THREAD_NUMBER];
   arg[0] = "hello world from thread1";
   arg[1] = "hello world from thread2";
   
   printf("Begin to create threads.../n");
   ret_val = pthread_create(&pt[0], NULL, hello1, (void *)arg[0]);
   if (ret_val != 0 ) {
      printf("pthread_create error!/n");
      exit(1);
   }

   ret_val = pthread_create(&pt[1], NULL, hello2, (void *)arg[1]);
   if (ret_val != 0 ) {
      printf("pthread_create error!/n");
      exit(1);
   }
   
   printf("Begin to wait for threads.../n");   
   for(i = 0; i < THREAD_NUMBER; i++) {
       ret_val = pthread_detach(pt[i]);
       if (ret_val != 0) {
          printf("pthread_join error!/n");
      exit(1);
       }
   }
   printf("Now, the main thread returns./n");
   return 0;
}

执行结果为

Begin to create threads...
Begin to wait for threads...
Now, the main thread returns. 
线程1,2没有执行(也可能执行),因为子线程为可分离的,主线程在执行完之后即将进程销毁,资源收回,导致子线程未运行。可以在return 0 语句之前加入sleep(5),这样执行结果为

Begin to create threads...
Begin to wait for threads...
Now, the main thread returns.
hello world from thread2
hello world from thread1 
所以,pthread_join()会挂起父线程,直至子线程完成才可以执行后面的代码,此外,一个PTHREAD_CREATE_JOINABLE状态的子线程不会自动释放该线程的内存资源,包括线程描述符和其使用的栈;而主线程调用pthread_detach()时,无需等待子线程的完成,它可以立即执行后面的代码,当然,也有可能主线程执行完之后销毁进程,导致子线程未能执行,此外,一个PTHREAD_CREATE_DETACH状态的子线程拥有自我回收内存资源的功能。

 

二、线程处于两种状态下,退出时(也就是调用pthread_exit()函数)。对资源的释放情况。

/******************************************

通过查看线程号,判断。线程退出时,是否释放了资源

**********************************************/

 

#include
#include
#include
#include

void *print_msg_fun(void *ptr)
{
 //pthread_detach(pthread_self());

/*保留此句表示线程处于 分离, 注释掉表示线程处于非分离*/
 printf("%d/n",pthread_self());


 pthread_exit(NULL);
}

int main()
{
 pthread_t th;

/*1  创建线程*/ 
 while(1)
 {
  if( 0 != (pthread_create(&th,NULL,print_msg_fun,NULL)))
  {
   perror("create th1");
   return;
  }

  sleep(3);
  printf("%d/n",th);
 }

}
通常是主线程使用pthread_create()创建子线程以后,一般可以调用pthread_detach(threadid)分离刚刚创建的子线程,这里的threadid是指子线程的threadid;如此以来,该子线程止时底层资源立即被回收;
    被创建的子线程也可以自己分离自己,子线程调用pthread_detach(pthread_self())就是分离自己,因为pthread_self()这个函数返回的就是自己本身的线程ID;

一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。


相关文章

  • CentOS 下 KVM QEMU 虚拟机 安装 配置 详解
    CentOS 下 KVM QEMU 虚拟机 安装 配置 详解 [日期:2015-08-14] 来源:Linux社区  作者:Linux [字体:大 中 小] 一,什么是KVM KVM包括很多部件:首先,它是一个Linux内核模块(现在包括在 ...
  • sysbench压力测试工具安装和参数介绍   一.sysbench压力测试工具简介:   sysbench是一个开源的.模块化的.跨平台的多线程性能测试工具,可以用来进行CPU.内存.磁盘I/O.线程.数据库的性能测试.目前支持的数据库有 ...
  • Java基础16:Java多线程基础最全总结
    这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时也分享技术干货和学习经验,致力于Ja ...
  • (与远程主机建立空管连接) net use \\ip地址\ipc$ "" /use:"" (以管理员身份登录远程主机) net use \\ip地址\ipc$ "密码" /use: ...
  • 目录 1. 限制最大容量:BoundedCapacity 2. 禁止加入:CompleteAdding和IsCompleted 3. 枚举:GetConsumingEnumerable和BlockingCollection本身 4. Get ...
  • 本系列意在记录Windwos线程的相关知识点,包括线程基础.线程调度.线程同步.TLS.线程池等 本篇介绍与内核对象同步相关的Event对象和Mutex对象   AutoResetEvent和ManualResetEvent 同步事件有两种 ...

2020 unjeep.com webmaster#unjeep.com
12 q. 0.013 s.
苏ICP备12049786号-20