一、线程退出
线程退出就是退出某一个线程而不影响其他线程的执行,这个函数主要在主线程中使用,因为子线程退出不会影响主线程的执行,但是主线程退出后,会销毁进程空间,所以本节讲的线程退出就是主线程执行退出后,不影响子线程的执行。
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 }
补充知识:
一、引用
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。
二、引用
线程正常终止的方法:
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.