(三)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.

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM