pthread_join函数

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);  同样的错误,即这样相当于传递的是子线程用户栈空间的地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++ Priemr | 15】派生类向基类转换的可访问性

1. 只有当D公有继承B时&#xff0c;用户代码才能使用派生类向基类的转换&#xff1b;如果D私有继承B的方式是受保护的或者私有的&#xff0c;则用户代码不能使用该转换。 class A {}&#xff1b; class B : public A {}void function(const A&) {}int main() {B b;functio…

pthread_detach函数

int pthread_detach(pthread_t thread); 成功&#xff1a;0&#xff1b;失败&#xff1a;错误号 作用&#xff1a;从状态上实现线程分离&#xff0c;注意不是指该线程独自占用地址空间。 线程分离状态&#xff1a;指定该状态&#xff0c;线程主动与主控线程断开关系。线程…

【C++ Primer | 15】面试问题

在成员函数中调用虚函数 #include <iostream> using namespace std; class CBase { public:void func1(){func2();}virtual void func2() {cout << "CBase::func2()" << endl;} }; class CDerived:public CBase { public:virtual void func2() {…

pthread_cancel、pthread_equal函数

&#xff08;1&#xff09;pthread_cancel函数 int pthread_cancel(pthread_t thread); 成功&#xff1a;0&#xff1b;失败&#xff1a;错误号 作用&#xff1a;杀死(取消)线程&#xff0c;其作用对应进程中 kill() 函数。 注意&#xff1a;线程的取消并不是实时的&…

STL源码剖析面试问题

当vector的内存用完了&#xff0c;它是如何动态扩展内存的&#xff1f;它是怎么释放内存的&#xff1f;用clear可以释放掉内存吗&#xff1f;是不是线程安全的&#xff1f; vector内存用完了&#xff0c;会以当前size大小重新申请2* size的内存&#xff0c;然后把原来的元素复制…

线程与进程的控制原语对比

线程与进程的控制原语对比 fork pthead_create exit( int ) pthead_exit(void *); wait(int *) pthread_join&#xff08; ,void **&#xff09; 阻塞 ;分离 22 &#xff1b;cancel -1 kill() pthread_cancel(); 取消点(检查点)&#xff1a;系统调用 getpid() pthrea…

ptmalloc堆内存管理机制(主要讨论Linux x86下32位系统)

bin&#xff08;chunk容器&#xff09; ptmalloc将相似大小的 chunk 用双向链表链接起来&#xff0c;这样的一个链表被称为一个 bin。 Ptmalloc 一共维护了 128 个 bin&#xff0c;并使用一个数组来存储这些 bin&#xff0c;这个数组被成为bin数组。 bin数组结构如下&#xf…

线程属性的修改

&#xff08;1&#xff09;线程属性 Linux下线程的属性是可以根据实际项目需要&#xff0c;进行设置&#xff0c;之前我们讨论的线程都是采用线程的默认属性&#xff0c;默认属性已经可以解决绝大多数开发时遇到的问题。如我们对程序的性能提出更高的要求那么需要设置线程属性…

NPTL(Native POSIX Thread Library)

1.NPTL&#xff08;Native POSIX Thread Library&#xff09;为POSIX标准线程库&#xff0c;查看当前Linux系统的pthread库&#xff08;线程库&#xff09;版本的命令为&#xff1a;getconf GNU_LIBPTHREAD_VERSION。 [rootlocalhost 01_pthread_test]# getconf GNU_LIBPTHREA…

线程使用注意事项

1.主线程退出其他线程不退出&#xff0c;主线程应调用pthread_exit&#xff1b; 2.避免僵尸线程&#xff1a;pthread_join、pthread_detach、pthread_create指定分离属性。被join线程可能在join函数返回前就释放完自己的所有内存资源&#xff0c;所以不应当返回被回收线程栈中…

线程同步的概念

所谓同步&#xff0c;即同时起步&#xff0c;协调一致。不同的对象&#xff0c;对“同步”的理解方式略有不同。如&#xff0c;设备同步&#xff0c;是指在两个设备之间规定一个共同的时间参考&#xff1b;数据库同步&#xff0c;是指让两个或多个数据库内容保持一致&#xff0…

互斥量(mutex)

Linux中提供一把互斥锁mutex&#xff08;也称之为互斥量&#xff09;。每个线程在对资源操作前都尝试先加锁&#xff0c;成功加锁才能操作&#xff0c;操作结束解锁。资源还是共享的&#xff0c;线程间也还是竞争的&#xff0c;但通过“锁”就将资源的访问变成互斥操作&#xf…

洗牌算法

参考资料&#xff1a; 1. 洗牌算法汇总以及测试洗牌程序的正确性 2. 三种洗牌算法shuffle

Bloom Filter算法

一、概念 Bloom Filter的中文翻译叫做布隆过滤器&#xff0c;是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法&#xff0c;缺点是有一定的误…

237. 删除链表中的节点

请编写一个函数&#xff0c;使其可以删除某个链表中给定的&#xff08;非末尾&#xff09;节点&#xff0c;你将只被给定要求被删除的节点。 现有一个链表 -- head [4,5,1,9]&#xff0c;它可以表示为: 示例 1: 输入: head [4,5,1,9], node 5 输出: [4,1,9] 解释: 给定你链表…

151. 翻转字符串里的单词

输入: " hello world! " 输出: "world! hello" 解释: 输入字符串可以在前面或者后面包含多余的空格&#xff0c;但是反转后的字符不能包括。 示例 3&#xff1a; 输入: "a good example" 输出: "example good a" 解释: 如果两个单…

进程间同步(互斥量、信号量)

进程间同步可以使用互斥量mutex&#xff08;互斥锁&#xff09;、信号量和文件锁。 进程间同步使用信号量&#xff1a; int sem_init(sem_t *sem, int pshared, unsigned int value); 用于进程间同步此时第二个参数不能取0了&#xff0c;取非0值用于进程间同步&#xff0c;一…

1059 Prime Factors(25 分)

Given any positive integer N, you are supposed to find all of its prime factors, and write them in the format N p​1​​​k​1​​​​p​2​​​k​2​​​​⋯p​m​​​k​m​​​​. Input Specification: Each input file contains one test case which gives a…

STL源码剖析

1. 当vector的内存用完了&#xff0c;它是如何动态扩展内存的&#xff1f;它是怎么释放内存的&#xff1f;用clear可以释放掉内存吗&#xff1f;是不是线程安全的&#xff1f; vector内存用完了&#xff0c;会以当前size大小重新申请2* size的内存&#xff0c;然后把原来的元素…

C++ 内存管理机制

内存分配方式 简介 在C中&#xff0c;内存分成5个区&#xff0c;他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 栈&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存…