回收子線程
pthread_join 函數
阻塞等待線程退出,擷取線程退出狀态 其作用,對應程序中
waitpid()
函數。
int pthread_join (pthread_t thread,void** retval);
成功:0,失敗:錯誤号
參數:thread:線程ID(注意 :不是指針);retval:存儲線程結束狀态
對比記憶:
- 程序中:main 傳回值、exit 參數–>int;等待子程序結束 wait 函數參數–>
int*
- 線程中:線程主函數傳回值、
;等待線程結束 pthread_join 函數參數–>pthread_exit-->void*
void**
- 對于程序而言,wait函數的傳回值是int,是以擷取退出值使用
int*
- 對于線程,
作為函數傳回值,回收使用void*
void**
示例1
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
typedef struct{
int a;
int b;
}exit_t;
void * tfn(void *arg) //子線程函數
{
//子線程函數中定義 ret;
exit_t *ret; //用結構體定義一個變量
ret=malloc(sizeof(exit_t));
ret->a = 100;
ret->b = 300;
//傳回ret 這個值,線程退出
pthread_exit((void *)ret);
}
int main(void)
{
pthread_t tid;
exit_t *retval;
pthread_create(&tid,NULL,tfn,NULL);
/*調用pthread_join可以擷取線程退出狀态*/
//第一個回收線程ID,第二個回收退出的值
pthread_join(tid,(void **)&retval); //wait(&status);
printf("a = %d,b = %d\n",retval->a,retval->b);
return 0;
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL1ADN3MzM1gTMyATOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
注意事項
調用該函數的線程将挂起等待,直到 id 為 thread 的線程終止。 thread 線程以不同的方法終止,通過 pthread_join 得到的終止狀态是不同的,總結如下:
- 如果 thread 線程通過 return 傳回,retval 所指向的單元裡存放的是 thread 線程函數的傳回值。
- 如果 thread 線程被别的線程調用 pthread_cancel 異常終止掉,retval 所指向的單元裡存放的是常數PTHREAD_CANCELED。
- 如果 thread 線程是自己調用 pthread_exit 終止的,retval 所指向的單元存放的是傳給 pthread_exit 的參數。
- 如果對 thread 線程的終止狀态不感興趣,可以傳 NULL 給 retval 參數。
示例2
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>
typedef struct {
char ch;
int var;
char str[64];
}exit_t;
void *thrd_func(void *arg)
{
//建立結構體變量
exit_t * retvar = (exit_t *)malloc(sizeof(exit_t));
//指派
retvar->ch='m';
retvar->var = 200;
strcpy(retvar->str,"我的傳回值");
//子線程退出
pthread_exit((void *)retvar);
}
int main(void)
{
pthread_t tid;
int ret;
exit_t * retval;
//主要線程ID
printf(" In main1 id = %lu,pid = %u\n",pthread_self(),getpid());
ret = pthread_create(&tid,NULL,thrd_func,NULL);
if(ret != 0){
fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
exit(1);
}
pthread_join(tid,(void **)&retval);
printf("子線程傳回值為\n");
printf("ch = %c ,var = %d,str = %s\n",retval->ch,retval->var,retval->str);
pthread_exit((void *)1);
return 0;
}
使用 pthread_join 函數将循環建立的多個子線程回收。
/*回收多個子線程*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
int var=100;
void *tfn(void *arg) //每個子線程進行回收
{
int i;
i = (int)arg;
sleep(i); //輸出有順序
if(i == 1){
var = 333;
printf("var = %d\n",var);
return (void *)var;
}else if(i == 3){
var = 777;
printf("I'm %d th 線程,線程ID為 %lu var = %d\n",i+1,pthread_self(),var);
pthread_exit((void *)var);
}else {
printf("I'm %d th 線程,線程ID = %lu\n var = %d\n",i+1,pthread_self(),var);
pthread_exit((void *)var);
}
return NULL;
}
int main(void)
{
pthread_t tid[5];
int i;
int *ret[5]; //儲存 5個線程的退出值
for(i = 0;i < 5; i++)//循環建立多個子線程
pthread_create(&tid[i],NULL,tfn,(void*)i);
for(i=0;i < 5; i++){ //對多個子線程進行回收
pthread_join(tid[i],(void **)&ret[i]);
printf("-----------%d th ret = %d\n",i,(int)ret[i]);
}
printf("I'm main 線程 tid = %lu\t var = %d\n",pthread_self(),var);
//主要線程也列印777,原因是共享全局變量
sleep(i);
return 0;
}
線程分離
pthread_detach 函數
程序當中沒有
實作線程分離
int pthread_detach(pthread_t thread);
成功:0;失敗:錯誤号
- 線程分離狀态:指定該狀态,線程主動與主要線程斷開關系。線程結束後,其退出狀态不由其他線程擷取,而 直接自己自動釋放。網絡、多線程伺服器常用。
-
程序若有該機制,将不會産生僵屍程序。僵屍程序的産生主要由于程序死後,大部分資源被釋放,一點殘留資 源仍存于系統中,導緻核心認為該程序仍存在。
也可使用 pthread_create 函數參 2(線程屬性)來設定線程分離。
使用 pthread_detach 函數實作線程分離
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
void *tfn(void *arg)
{
int n=3;
while(n--){
printf("thread count %d\n",n);
sleep(1);
}
return (void *)1;
}
int main(void)
{
pthread_t tid;
void *tret;
int err;
pthread_create(&tid,NULL,tfn,NULL);
pthread_detach(tid); //讓線程分離 ----自動退出,無系統殘留資源
while(1){
err = pthread_join(tid,&tret);
printf("---------err= %d\n",err);
if(err != 0)
fprintf(stderr,"thread error: %s\n",strerror(err));
else
fprintf(stderr,"thread exit code %d\n",(int)tret);
sleep(1);
}
return 0;
}
注意事項
一般情況下,線程終止後,其終止狀态一直保留到其它線程調用 pthread_join 擷取它的狀态為止。但是線程也 可以被置為 detach 狀态,這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀态。**不能對一個已 經處于 detach 狀态的線程調用 pthread_join,這樣的調用将傳回 EINVAL 錯誤。**也就是說,如果已經對一個線程調用 了 pthread_detach 就不能再調用 pthread_join 了。
殺死線程
pthread_cancel 函數
殺死(取消)線程 其作用,對應程序中 kill() 函數。
int pthread_cancel(pthread_t thread);
成功:0;失敗:錯誤号
注意:線程的取消并不是實時的,而有一定的延時。需要等待線程到達某個取消點(檢查點)。
- 類似于玩遊戲存檔,必須到達指定的場所(存檔點,如:客棧、倉庫、城裡等)才能存儲進度。殺死線程也不是 立刻就能完成,必須要到達取消點。
- 取消點:是線程檢查是否被取消,并按請求進行動作的一個位置。通常是一些系統調用
執行指令creat,open,pause, close,read,write.....
可以檢視具備這些取消點的系統調用清單。也可參閱man 7 pthreads
APUE.12.7
取消選項小節。
可粗略認為一個系統調用(進入核心)即為一個取消點。如線程中沒有取消點,可以通過調用 pthreestcancel 函數 自行設定一個取消點。
- 被取消的線程, 退出值定義在Linux的pthread庫中。常數PTHREAD_CANCELED的值是**-1**。可在頭檔案pthread.h 中找到它的定義:
。是以當我們對一個已經被取消的線程使用#define PTHREAD_CANCELED((void*)-1)
回收時,得到的傳回值為-1。pthread_join
終止線程的三種方法
/*三種退出線程的方法*/
#include<stdio.h>
#include<string.h>
#include<pthread.h>
void *tfn1(void *arg) //第一個線程
{
printf("thread 1 returning\n");
return (void *)111;
}
void *tfn2(void *arg) //第二個線程
{
printf("thread 2 exiting\n");
pthread_exit((void *)222);
}
void *tfn3(void *arg) //第三個線程
{
while(1){
printf("thread 3:I'm going to die in 3 seconds ....\n");
sleep(1);
//pthread_testcancel();//自己添加取消點
}
return (void *)666;
}
int main(void)
{
pthread_t tid;
void *tret = NULL;
pthread_create(&tid,NULL,tfn1,NULL);
pthread_join(tid,&tret);
printf("thread 1 exit code = %d\n\n",(int)tret);
pthread_create(&tid,NULL,tfn2,NULL);
pthread_join(tid,&tret);
printf("thread 2 exit code = %d\n\n",(int)tret);
pthread_create(&tid,NULL,tfn3,NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid,&tret);
printf("thread 3 exit code = %d\n\n",(int)tret);
return 0;
}
當把一個線程殺死後,它的傳回值是-1;
終止線程方式
總結:終止某個線程而不終止整個程序,
有三種方法:
- 從線程主函數 return。這種方法對主要線程不适用,從 main 函數 return 相當于調用 exit。
- 一個線程可以調用 pthread_cancel 終止同一程序中的另一個線程。
- 線程可以調用 pthread_exit 終止自己。
pthread_equal 函數
比較兩個線程 ID 是否相等。
int pthread_equal (pthread_t t1,pthread_t t2);
有可能 Linux 在未來線程
ID pthread_t
類型被修改為結構體實作。