pthread_join函數

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

作用:阻塞等待線程退出,獲取線程退出狀態。其作用對應進程中 waitpid() 函數。

成功:0;失敗:錯誤號 ??strerror函數

參數:thread:線程ID (注意:不是指針);retval:存儲線程結束狀態。

對比記憶:在wait回收子進程時,子進程exit或return返回的值即為status的值(int類型),wait函數第一個形參為回收進程的pid,第二個形參為&status(int *類型指針);對于回收子線程時,子線程退出的值(pthread_exit函數,或return返回的值)都為void *類型,則pthread_join函數的第一個形參為線程ID(pthread_t類型),第二個形參為void **類型。

調用該函數的線程將掛起等待,直到idthread的線程終止。thread線程以不同的方法終止,通過pthread_join得到的終止狀態是不同的,總結如下:

  1. 如果thread線程通過return返回,retval所指向的單元里存放的是thread線程函數的返回值(即void *指針);
  2. 如果thread線程被別的線程調用pthread_cancel異常終止掉,retval所指向的單元里存放的是常數PTHREAD_CANCELED。
  3. 如果thread線程是自己調用pthread_exit終止的,retval所指向的單元存放的是傳給pthread_exit的參數(即void *指針),其實1與3是等效的。
  4. 如果對thread線程的終止狀態不感興趣,可以傳NULL給retval參數。

對應于僵尸進程,也是存在僵尸線程。因此,在線程結束時,也需要對線程進行回收以釋放僵尸線程所占用的資源。對進程的回收有區別的就是,一個進程中的所有線程都是可以互相回收的,即不是只能由主控線程回收子線程,兄弟線程之間也可以回收。一次pthread_join只能回收一個線程。回收線程時也可以不關心線程結束時的狀態,pthread_join函數的參數傳NULL即可,此時不關心線程結束時的狀態,只是將其回收并釋放線程所占用的資源。

//循環創建n個子線程,并回收這n個子線程

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>typedef struct {int a;char b;char str[10];
}exit_t;    //返回值為一個結構體void *ftn( void *arg )
{int s;exit_t *retval;   //retval的地址在棧空間,但是retval本身卻位與堆空間(heap),malloc所分配的。retval=(exit_t *)malloc( sizeof(exit_t) );  //必須用malloc分配,不能用線程棧空間s=(int)arg;  //注意只能傳值,不能傳地址(用于判別是哪個線程)if( s<=1 ){retval->a=10;retval->b='a';strcpy(retval->str,"zsx");}if( s>1 && s<=3 ){retval->a=20;retval->b='b';strcpy(retval->str,"rgf");}if( s>3 ){retval->a=30;retval->b='c';strcpy(retval->str,"zy");}printf("I am %dth thread, and my ID is %lu.\n",s+1,pthread_self( ));pthread_exit((void *)retval); //或者 return (void *)retval; 兩者等價!
}int main(int argc, char *argv[ ])
{int n=5, i, ret;if( argc==2 )n=atoi(argv[1]);pthread_t tid[n];for(i=0;i<n;i++){ret=pthread_create(&tid[i],NULL,ftn,(void *)i);  //注意只能傳值,不能傳地址,因為地址對應的i值會變化if( ret!=0){fprintf(stderr,"pthread_create error: %s\n",strerror(ret));exit(1);}}for(i=0;i<n;i++)   //回收每個子線程{exit_t *re;  //re位于主控線程的棧空間,但是re本身的值為子線程傳給它的值。ret=pthread_join( tid[i],(void **)&re);if( ret!=0){fprintf(stderr,"pthread_join error: %s\n",strerror(ret));exit(1);}printf("the %dth thread: a=%d, b=%c, str=%s.\n",i+1,re->a,re->b,re->str);free(re);  //注意必須釋放malloc分配的空間,防止內存泄漏re = NULL;  //置空,防止使用幽靈指針}printf("In main: the PID is %d, and the TID is %lu.\n",getpid( ),pthread_self( ));pthread_exit((void *)1);
}

[root@localhost 01_pthread_test]# ./ptrd_join

I am 4th thread, and my ID is 4124101440.

I am 2th thread, and my ID is 4140886848.

I am 1th thread, and my ID is 4149279552.

I am 5th thread, and my ID is 4115708736.

I am 3th thread, and my ID is 4132494144.

the 1th thread: a=10, b=a, str=zsx.

the 2th thread: a=10, b=a, str=zsx.

the 3th thread: a=20, b=b, str=rgf.

the 4th thread: a=20, b=b, str=rgf.

the 5th thread: a=30, b=c, str=zy.

In main: the PID is 10415, and the TID is 4151490816.

分析討論:

  1. 強調一點:子線程的返回值為void *指針,其指針所指向的地址必須位于全局區或者malloc分配的空間,不能位于用戶棧空間;
  2. 之所以子線程的返回值以及線程函數ftn的形參都要為void *類型,這是為了方便可以傳遞任何數據類型;
  3. 其實回收子線程的返回值的方式有很多種:a.第一種方式:上述程序中,exit_t *retval; 然后直接返回retval這個地址給主控線程,但是地址不能是用戶棧空間,因此用malloc函數分配空間后,再賦值返回;b.第二種方式:也可以exit_t retval,即直接返回retval本身這個值,即return (void *)retval; 主控線程中:exit_t *re; (exit_t)re即可,但是不建議這樣做,兼容性不高。這種方法類似于上面的i傳參方式;c.第三種方式:還是按值傳遞,即利用主控線程中的pthread_create函數中的最后一個參數,將值傳給子線程,然后讓子線程返回回來,即:主控線程: exit_t *re=(exit_t *)malloc( sizeof(exit_t) ), 然后(void *)re作為其最后一個參數傳遞給子線程的arg參數,則子線程中:(exit_t *)arg,然后給arg賦值,最后(void *)arg返回即可!
  4. 注意在線程函數中:pthread_exit((void *)retval);不能寫為:pthread_exit((void *)&retval);? 同樣的錯誤,即這樣相當于傳遞的是子線程用戶棧空間的地址。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385293.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385293.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385293.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C++ Priemr | 15】派生類向基類轉換的可訪問性

1. 只有當D公有繼承B時&#xff0c;用戶代碼才能使用派生類向基類的轉換&#xff1b;如果D私有繼承B的方式是受保護的或者私有的&#xff0c;則用戶代碼不能使用該轉換。 class A {}&#xff1b; class B : public A {}void function(const A&) {}int main() {B b;functio…

pthread_detach函數

int pthread_detach(pthread_t thread); 成功&#xff1a;0&#xff1b;失敗&#xff1a;錯誤號 作用&#xff1a;從狀態上實現線程分離&#xff0c;注意不是指該線程獨自占用地址空間。 線程分離狀態&#xff1a;指定該狀態&#xff0c;線程主動與主控線程斷開關系。線程…

【C++ Primer | 15】面試問題

在成員函數中調用虛函數 #include <iostream> using namespace std; class CBase { public:void func1(){func2();}virtual void func2() {cout << "CBase::func2()" << endl;} }; class CDerived:public CBase { public:virtual void func2() {…

pthread_cancel、pthread_equal函數

&#xff08;1&#xff09;pthread_cancel函數 int pthread_cancel(pthread_t thread); 成功&#xff1a;0&#xff1b;失敗&#xff1a;錯誤號 作用&#xff1a;殺死(取消)線程&#xff0c;其作用對應進程中 kill() 函數。 注意&#xff1a;線程的取消并不是實時的&…

STL源碼剖析面試問題

當vector的內存用完了&#xff0c;它是如何動態擴展內存的&#xff1f;它是怎么釋放內存的&#xff1f;用clear可以釋放掉內存嗎&#xff1f;是不是線程安全的&#xff1f; vector內存用完了&#xff0c;會以當前size大小重新申請2* size的內存&#xff0c;然后把原來的元素復制…

線程與進程的控制原語對比

線程與進程的控制原語對比 fork pthead_create exit( int ) pthead_exit(void *); wait(int *) pthread_join&#xff08; ,void **&#xff09; 阻塞 ;分離 22 &#xff1b;cancel -1 kill() pthread_cancel(); 取消點(檢查點)&#xff1a;系統調用 getpid() pthrea…

ptmalloc堆內存管理機制(主要討論Linux x86下32位系統)

bin&#xff08;chunk容器&#xff09; ptmalloc將相似大小的 chunk 用雙向鏈表鏈接起來&#xff0c;這樣的一個鏈表被稱為一個 bin。 Ptmalloc 一共維護了 128 個 bin&#xff0c;并使用一個數組來存儲這些 bin&#xff0c;這個數組被成為bin數組。 bin數組結構如下&#xf…

線程屬性的修改

&#xff08;1&#xff09;線程屬性 Linux下線程的屬性是可以根據實際項目需要&#xff0c;進行設置&#xff0c;之前我們討論的線程都是采用線程的默認屬性&#xff0c;默認屬性已經可以解決絕大多數開發時遇到的問題。如我們對程序的性能提出更高的要求那么需要設置線程屬性…

NPTL(Native POSIX Thread Library)

1.NPTL&#xff08;Native POSIX Thread Library&#xff09;為POSIX標準線程庫&#xff0c;查看當前Linux系統的pthread庫&#xff08;線程庫&#xff09;版本的命令為&#xff1a;getconf GNU_LIBPTHREAD_VERSION。 [rootlocalhost 01_pthread_test]# getconf GNU_LIBPTHREA…

線程使用注意事項

1.主線程退出其他線程不退出&#xff0c;主線程應調用pthread_exit&#xff1b; 2.避免僵尸線程&#xff1a;pthread_join、pthread_detach、pthread_create指定分離屬性。被join線程可能在join函數返回前就釋放完自己的所有內存資源&#xff0c;所以不應當返回被回收線程棧中…

線程同步的概念

所謂同步&#xff0c;即同時起步&#xff0c;協調一致。不同的對象&#xff0c;對“同步”的理解方式略有不同。如&#xff0c;設備同步&#xff0c;是指在兩個設備之間規定一個共同的時間參考&#xff1b;數據庫同步&#xff0c;是指讓兩個或多個數據庫內容保持一致&#xff0…

互斥量(mutex)

Linux中提供一把互斥鎖mutex&#xff08;也稱之為互斥量&#xff09;。每個線程在對資源操作前都嘗試先加鎖&#xff0c;成功加鎖才能操作&#xff0c;操作結束解鎖。資源還是共享的&#xff0c;線程間也還是競爭的&#xff0c;但通過“鎖”就將資源的訪問變成互斥操作&#xf…

洗牌算法

參考資料&#xff1a; 1. 洗牌算法匯總以及測試洗牌程序的正確性 2. 三種洗牌算法shuffle

Bloom Filter算法

一、概念 Bloom Filter的中文翻譯叫做布隆過濾器&#xff0c;是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法&#xff0c;缺點是有一定的誤…

237. 刪除鏈表中的節點

請編寫一個函數&#xff0c;使其可以刪除某個鏈表中給定的&#xff08;非末尾&#xff09;節點&#xff0c;你將只被給定要求被刪除的節點。 現有一個鏈表 -- head [4,5,1,9]&#xff0c;它可以表示為: 示例 1: 輸入: head [4,5,1,9], node 5 輸出: [4,1,9] 解釋: 給定你鏈表…

151. 翻轉字符串里的單詞

輸入: " hello world! " 輸出: "world! hello" 解釋: 輸入字符串可以在前面或者后面包含多余的空格&#xff0c;但是反轉后的字符不能包括。 示例 3&#xff1a; 輸入: "a good example" 輸出: "example good a" 解釋: 如果兩個單…

進程間同步(互斥量、信號量)

進程間同步可以使用互斥量mutex&#xff08;互斥鎖&#xff09;、信號量和文件鎖。 進程間同步使用信號量&#xff1a; int sem_init(sem_t *sem, int pshared, unsigned int value); 用于進程間同步此時第二個參數不能取0了&#xff0c;取非0值用于進程間同步&#xff0c;一…

1059 Prime Factors(25 分)

Given any positive integer N, you are supposed to find all of its prime factors, and write them in the format N p?1???k?1????p?2???k?2?????p?m???k?m????. Input Specification: Each input file contains one test case which gives a…

STL源碼剖析

1. 當vector的內存用完了&#xff0c;它是如何動態擴展內存的&#xff1f;它是怎么釋放內存的&#xff1f;用clear可以釋放掉內存嗎&#xff1f;是不是線程安全的&#xff1f; vector內存用完了&#xff0c;會以當前size大小重新申請2* size的內存&#xff0c;然后把原來的元素…

C++ 內存管理機制

內存分配方式 簡介 在C中&#xff0c;內存分成5個區&#xff0c;他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區。 棧&#xff1a;在執行函數時&#xff0c;函數內局部變量的存儲單元都可以在棧上創建&#xff0c;函數執行結束時這些存儲單元自動被釋放。棧內存…