一、線程退出
線程退出就是退出某一個線程而不影響其他線程的執行,這個函數主要在主線程中使用,因為子線程退出不會影響主線程的執行,但是主線程退出后,會銷毀進程空間,所以本節講的線程退出就是主線程執行退出后,不影響子線程的執行。
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. |