int pthread_join(pthread_t thread, void **retval);

作用:阻塞等待线程退出,获取线程退出状态。其作用对应进程中 waitpid() 函数。

成功:0;失败:错误号   strerror函数

参数:thread:线程ID (注意:不是指针);retval:存储线程结束状态。

对比记忆:在wait回收子进程时,子进程exit或return返回的值即为status的值(int类型),wait函数第一个形参为回收进程的pid,第二个形参为&status(int *类型指针);对于回收子线程时,子线程退出的值(pthread_exit函数,或return返回的值)都为void *类型,则pthread_join函数的第一个形参为线程ID(pthread_t类型),第二个形参为void **类型。

调用该函数的线程将挂起等待,直到idthread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

  1. 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值(即void *指针);
  2. 如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。
  3. 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数(即void *指针),其实1与3是等效的。
  4. 如果对thread线程的终止状态不感兴趣,可以传NULL给retval参数。

对应于僵尸进程,也是存在僵尸线程。因此,在线程结束时,也需要对线程进行回收以释放僵尸线程所占用的资源。对进程的回收有区别的就是,一个进程中的所有线程都是可以互相回收的,即不是只能由主控线程回收子线程,兄弟线程之间也可以回收。一次pthread_join只能回收一个线程。回收线程时也可以不关心线程结束时的状态,pthread_join函数的参数传NULL即可,此时不关心线程结束时的状态,只是将其回收并释放线程所占用的资源。

//循环创建n个子线程,并回收这n个子线程

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>typedef struct {int a;char b;char str[10];
}exit_t;    //返回值为一个结构体void *ftn( void *arg )
{int s;exit_t *retval;   //retval的地址在栈空间,但是retval本身却位与堆空间(heap),malloc所分配的。retval=(exit_t *)malloc( sizeof(exit_t) );  //必须用malloc分配,不能用线程栈空间s=(int)arg;  //注意只能传值,不能传地址(用于判别是哪个线程)if( s<=1 ){retval->a=10;retval->b='a';strcpy(retval->str,"zsx");}if( s>1 && s<=3 ){retval->a=20;retval->b='b';strcpy(retval->str,"rgf");}if( s>3 ){retval->a=30;retval->b='c';strcpy(retval->str,"zy");}printf("I am %dth thread, and my ID is %lu.\n",s+1,pthread_self( ));pthread_exit((void *)retval); //或者 return (void *)retval; 两者等价!
}int main(int argc, char *argv[ ])
{int n=5, i, ret;if( argc==2 )n=atoi(argv[1]);pthread_t tid[n];for(i=0;i<n;i++){ret=pthread_create(&tid[i],NULL,ftn,(void *)i);  //注意只能传值,不能传地址,因为地址对应的i值会变化if( ret!=0){fprintf(stderr,"pthread_create error: %s\n",strerror(ret));exit(1);}}for(i=0;i<n;i++)   //回收每个子线程{exit_t *re;  //re位于主控线程的栈空间,但是re本身的值为子线程传给它的值。ret=pthread_join( tid[i],(void **)&re);if( ret!=0){fprintf(stderr,"pthread_join error: %s\n",strerror(ret));exit(1);}printf("the %dth thread: a=%d, b=%c, str=%s.\n",i+1,re->a,re->b,re->str);free(re);  //注意必须释放malloc分配的空间,防止内存泄漏re = NULL;  //置空,防止使用幽灵指针}printf("In main: the PID is %d, and the TID is %lu.\n",getpid( ),pthread_self( ));pthread_exit((void *)1);
}

[root@localhost 01_pthread_test]# ./ptrd_join

I am 4th thread, and my ID is 4124101440.

I am 2th thread, and my ID is 4140886848.

I am 1th thread, and my ID is 4149279552.

I am 5th thread, and my ID is 4115708736.

I am 3th thread, and my ID is 4132494144.

the 1th thread: a=10, b=a, str=zsx.

the 2th thread: a=10, b=a, str=zsx.

the 3th thread: a=20, b=b, str=rgf.

the 4th thread: a=20, b=b, str=rgf.

the 5th thread: a=30, b=c, str=zy.

In main: the PID is 10415, and the TID is 4151490816.

分析讨论:

  1. 强调一点:子线程的返回值为void *指针,其指针所指向的地址必须位于全局区或者malloc分配的空间,不能位于用户栈空间;
  2. 之所以子线程的返回值以及线程函数ftn的形参都要为void *类型,这是为了方便可以传递任何数据类型;
  3. 其实回收子线程的返回值的方式有很多种:a.第一种方式:上述程序中,exit_t *retval; 然后直接返回retval这个地址给主控线程,但是地址不能是用户栈空间,因此用malloc函数分配空间后,再赋值返回;b.第二种方式:也可以exit_t retval,即直接返回retval本身这个值,即return (void *)retval; 主控线程中:exit_t *re; (exit_t)re即可,但是不建议这样做,兼容性不高。这种方法类似于上面的i传参方式;c.第三种方式:还是按值传递,即利用主控线程中的pthread_create函数中的最后一个参数,将值传给子线程,然后让子线程返回回来,即:主控线程: exit_t *re=(exit_t *)malloc( sizeof(exit_t) ), 然后(void *)re作为其最后一个参数传递给子线程的arg参数,则子线程中:(exit_t *)arg,然后给arg赋值,最后(void *)arg返回即可!
  4. 注意在线程函数中:pthread_exit((void *)retval);不能写为:pthread_exit((void *)&retval);  同样的错误,即这样相当于传递的是子线程用户栈空间的地址。

pthread_join函数相关推荐

  1. pthread_join()函数理解

    pthread_t tid; 使用方式:pthread_join(tid,NULL); 为什么要使用pthread_join()? 在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时 ...

  2. pthread_join()函数理解实验

    原址 一.使用方式 pthread_t tid; pthread_create(&tid, NULL, thread_run,NULL); pthread_join(tid,NULL); 创建 ...

  3. linux之使用 pthread_join 函数将循环创建的多个子线程回收

    代码:pthrd_loop_join.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #i ...

  4. OS / Linux / pthread_join() 和 pthread_detach() 函数区别

    一.线程的两种状态 对于 linux 线程来说,pthread 有两种状态:joinable 和 unjoinable. 若线程的状态是 joinable,当线程函数自己返回退出时或 pthread_ ...

  5. 多线程pthread_join()的作用

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/yzy1103203312/articl ...

  6. linux中pthread_join()与pthread_detach()详解

    前言: 1.linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_e ...

  7. 《APUE》中的函数整理

    第1章 unix基础知识 1. char *strerror(int errnum) 该函数将errnum(就是errno值)映射为一个出错信息字符串,返回该字符串指针.声明在string.h文件中. ...

  8. 多线程之pthread_create()函数

    总述:pthread_create是(Unix.Linux.Mac OS X)等操作系统的创建线程的函数.它的功能是创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线 ...

  9. pthread_join和pthread_detach详解

    在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反, ...

最新文章

  1. 测试方案_何小伟:ABTest测试方案
  2. 算法竞赛知识合集 目录(博客中转站)
  3. VC/MFC分割字符串(SplitString)返回CStringArray
  4. Android 更新项目名称
  5. Oracle-Listener log解读
  6. 合并两个有序数组为一个新的有序数组
  7. java8 Optional源码
  8. 骑马与砍杀是用python编的_骑马与砍杀编辑模式怎么用
  9. mysql未监控在3306_监控MySQL或Web服务是否正常
  10. java 解析二进制_Java二进制指令代码解析
  11. 可视化操作和命令化操作
  12. idea使用数据库连接工具
  13. 使用apache.commons.fileupload 进行文件上传
  14. Linux安全加固 附脚本
  15. 学习笔记14--车联网辅助定位技术
  16. 自动驾驶仿真相关调研
  17. 计算机改显存会有啥影响,显卡内存越大越好吗?显存对计算机速度(全文)的影响...
  18. 人工智能初学笔记系列(1):基础概念与进化流程
  19. HTML+CSS+移动端前端
  20. CISP证书的基本常识

热门文章

  1. 【SpringMVC】SpringMVC系列4之@RequestParam 映射请求参数值
  2. fis 详细介绍(mac版) - 12-26没有弄完 - 暂停
  3. Oracle中用For Loop 替代Cursor
  4. 部署环境_Hyperledger Fabric Composer环境部署(一)
  5. python列表姓氏_python数据分析实例(六) 中国姓氏数据
  6. Oracle 常用的一些函数
  7. c++ 基本排序算法学习
  8. Brush notes:stack、queue、heap
  9. Java集合unmodifiableSortedSet()方法(带示例)
  10. java类名与文件名_为什么Java文件名必须与公共类名相同?