回收子線程
pthread_join 函數
阻塞等待線程退出,獲取線程退出狀態 其作用,對應進程中 waitpid()
函數。
int pthread_join (pthread_t thread,void** retval);
成功:0,失敗:錯誤號
參數:thread:線程ID(注意 :不是指針);retval:存儲線程結束狀態
對比記憶:
- 進程中:main 返回值、exit 參數–>int;等待子進程結束 wait 函數參數–>
int*
- 線程中:線程主函數返回值、
pthread_exit-->void*
;等待線程結束 pthread_join 函數參數–>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;
}
注意事項
調用該函數的線程將掛起等待,直到 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;//主控線程IDprintf(" 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)); elsefprintf(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)
。因此當我們對一個已經被取消的線程使用pthread_join
回收時,得到的返回值為-1。
終止線程的三種方法
/*三種退出線程的方法*/ #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
類型被修改為結構體實現。