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