(三)linux线程编程学习笔记——线程退出、线程回收

一、线程退出       

线程退出就是退出某一个线程而不影响其他线程的执行,这个函数主要在主线程中使用,因为子线程退出不会影响主线程的执行,但是主线程退出后,会销毁进程空间,所以本节讲的线程退出就是主线程执行退出后,不影响子线程的执行。

void pthread_exit(void *retval);

参数是一个传出参数,可以用于其他线程,如果不需要,也可以传递NULL

代码如下:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<pthread.h>
 4 #include<unistd.h>
 5 void* callback(void* arg){
 6    printf("子线程id:%ld\n",pthread_self());
 7    for(int i=0;i<5;i++){
 8      printf("子线程:%d\n",i);
 9    }
10    return NULL;
11 };
12 int main(){
13         pthread_t tid;
14         pthread_create(&tid,NULL,callback,NULL);
15         printf("主线程id:%ld\n ",pthread_self());
16         for(int i=0;i<5;i++){
17           printf("主线程:%d\n",i);
18         }
19   pthread_exit(NULL);
20   return 0;
21 }

二、线程回收

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

解释:主线程回收子线程资源

参数:

pthread_t thread:需要回收的子线程id

参数:

   void **retval:保存传出值的地址,如果不需要传出值,就给NULL

不是所有的子线程资源都需要主线程回收,只是负责回收子线程内核部分的资源,需要主线程帮助子线程回收,子线程结束后,会自己释放栈区数据,但内核部分不会自动释放

该函数执行后就处于阻塞等待子线程的退出,如果子线程不退出,该函数就一直等待,而且该函数每调用一次,只回收一个子线程的资源,也就是说假如有10个线程,调用该函数后不是将这10个线程资源全部回收。如果要将全部线程资源回收,需要循环执行该函数。

另外,该函数执行后可以接收子线程结束后传出的数据,如在线程创建时,pthrea_create函数的第三个参数void *(*start_routine) (void *)是一个函数,该函数即使有返回值,主线程也是接收不到的,那么如何接收到子线程返回值呢?可以通过在主线程中调用pthread_exit在参数中将数据传出来,谁回收这个子线程,谁就会得到该返回值。

三、练习

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<pthread.h>
 4 #include<unistd.h>
 5 struct Test{
 6   int num;
 7   int age;
 8 };
 9 void* callback(void* arg){
10    printf("子线程id:%ld\n",pthread_self());
11   struct Test t;
12    t.num=100;
13    t.age=30;
14    pthread_exit(&t);
15    return NULL;
16 };
17 int main(){
18     pthread_t tid;
19     pthread_create(&tid,NULL,callback,NULL);
20         printf("主线程id:%ld\n ",pthread_self());
21     void *ptr;
22     pthread_join(tid,&ptr);
23     struct Test* pt=(struct Test*)ptr;
24     printf("num:%d,age=%d\n",pt->num,pt->age);
25   return 0;
26 }

执行结果:

 

 

 此时会发现和我们想要的结果不一样,原因如下:

我们在子线程中声明的变量 :struct Test t是一个局部变量,局部变量在函数执行完毕后会被系统回收,所以即使外面指向了那块地址,但是内容已不是原来的内容了

解决办法如下:

保证局部变量在函数结束后不被释放就可以了,也就是将局部变量定义为全局变量会堆内存变量即可,修改如下:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<pthread.h>
 4 #include<unistd.h>
 5 struct Test{
 6   int num;
 7   int age;
 8 };
 9 
10   struct Test t;//将局部变量修改为全局变量
11 void* callback(void* arg){
12    printf("子线程id:%ld\n",pthread_self());
13    t.num=100;
14    t.age=30;
15    pthread_exit(&t);
16    return NULL;
17 };
18 int main(){
19     pthread_t tid;
20     pthread_create(&tid,NULL,callback,NULL);
21         printf("主线程id:%ld\n ",pthread_self());
22     void *ptr;
23     pthread_join(tid,&ptr);
24     struct Test* pt=(struct Test*)ptr;
25     printf("num:%d,age=%d\n",pt->num,pt->age);
26   return 0;
27 }

 

还一种解决办法是使用主线程的栈空间,也就是在主线程中定义一个变量,然后在创建子线程时,将该变量作为参数传递给线程创建函数,修改如下:

 

 

代码如下:



 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<pthread.h>
 4 #include<unistd.h>
 5 struct Test{
 6   int num;
 7   int age;
 8 };
 9 
10   struct Test t;
11 void* callback(void* arg){
12    printf("子线程id:%ld\n",pthread_self());
13    struct Test* t=(struct Test*)arg;
14    t->num=100;
15    t->age=30;
16    pthread_exit(&t);
17    return NULL;
18 };
19 int main(){
20         pthread_t tid;
21         struct Test t;
22         pthread_create(&tid,NULL,callback,&t);
23         printf("主线程id:%ld\n ",pthread_self());
24         pthread_join(tid,NULL);
25         printf("num:%d,age=%d\n",t.num,t.age);
26   return 0;
27 }

 

 补充知识:

 一、引用自:https://blog.csdn.net/zhou1021jian/article/details/71531699

void pthread_exit( void * value_ptr );
线程的终止可以是调用了pthread_exit或者该线程的例程结束。也就是说,一个线程可以隐式的退出,也可以显式的调用pthread_exit函数来退出。
pthread_exit函数唯一的参数value_ptr是函数的返回代码,只要pthread_join中的第二个参数value_ptr不是NULL,这个值将被传递给value_ptr。
函数原型如下:
int pthread_join( pthread_t  thread, void * * value_ptr );
函数pthread_join的作用是,等待一个线程终止。
调用pthread_join的线程将被挂起直到参数thread所代表的线程终止时为止。pthread_join是一个线程阻塞函数,调用它的函数将一直等到被等待的线程结束为止。
如果value_ptr不为NULL,那么线程thread的返回值存储在该指针指向的位置。该返回值可以是由pthread_exit给出的值,或者该线程被取消而返回PTHREAD_CANCELED。

二、引用自https://www.cnblogs.com/zhangxuan/p/6430034.html

线程正常终止的方法:

1、return从线程函数返回。

2、通过调用函数pthread_exit使线程退出

3. 线程可以被同一进程中的其他线程取消。

 

主线程、子线程调用exit, pthread_exit,互相产生的影响。

1、在主线程中,在main函数中return了或是调用了exit函数,则主线程退出,且整个进程也会终止,

此时进程中的所有线程也将终止。因此要避免main函数过早结束。

2、在主线程中调用pthread_exit,   则仅仅是主线程结束,进程不会结束,进程内的其他线程也不会结束,

知道所有线程结束,进程才会终止。

3、在任何一个线程中调用exit函数都会导致进程结束。进程一旦结束,那么进程中的所有线程都将结束。

 

为什么要使用pthread_join?

线程终止最重要的问题是资源释放的问题。

线程终止时需要注意线程同步的问题。一般情况下,进程中各个线程的运行是相互独立的,线程的终止不会相互通知,也不会影响其他线程,

终止的线程所占用的资源不会随着线程的结束而归还系统,而是仍为线程所在的进程持有。在Linux中,默认情况下是在一个线程被创建后,

必须使用此函数对创建的线程进行资源回收,但是可以设置Threads attributes来设置当一个线程结束时,直接回收此线程所占用的系统资源,详细资料查看Threads attributes。

 

函数pthread_join用来等待一个线程的结束,pthread_join的调用者将被挂起并等待thread线程终止。需要注意的是一个线程仅允许一个线程使用pthread_join

等待它结束,并且被等待的线程应该处于可join状态。即非DETACHED状态。DETACHED是指某个线程执行pthread_detach后所处的状态。处于DETACHED状态

的线程无法由pthread_join同步。

一个可pthread_join的线程所占用的资源仅当有线程对其执行了pthread_join后才会释放,因此为了防止内存泄漏,所有线程终止时,要么已经被设置为DETACHED状态

要么使用pthread_join来回收资源。

notice:

一个线程不能被多个线程等待。否则第一个收到信号的线程成功返回。其余调用pthread_join的线程返回错误码

ESRCH  No thread with the ID thread could be found.

 

 

 

 

 

 

 

 

 

posted @ 2021-05-20 13:29  疯狗强尼  阅读(1520)  评论(0编辑  收藏  举报