天天看點

Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程

回收子線程

pthread_join 函數

阻塞等待線程退出,擷取線程退出狀态 其作用,對應程序中

waitpid()

函數。

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

成功:0,失敗:錯誤号

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

對比記憶:

  1. 程序中:main 傳回值、exit 參數–>int;等待子程序結束 wait 函數參數–>

    int*

  2. 線程中:線程主函數傳回值、

    pthread_exit-->void*

    ;等待線程結束 pthread_join 函數參數–>

    void**

  3. 對于程序而言,wait函數的傳回值是int,是以擷取退出值使用

    int*

  4. 對于線程,

    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;
}
           
Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程

注意事項

調用該函數的線程将挂起等待,直到 id 為 thread 的線程終止。 thread 線程以不同的方法終止,通過 pthread_join 得到的終止狀态是不同的,總結如下:

  1. 如果 thread 線程通過 return 傳回,retval 所指向的單元裡存放的是 thread 線程函數的傳回值。
  2. 如果 thread 線程被别的線程調用 pthread_cancel 異常終止掉,retval 所指向的單元裡存放的是常數PTHREAD_CANCELED。
  3. 如果 thread 線程是自己調用 pthread_exit 終止的,retval 所指向的單元存放的是傳給 pthread_exit 的參數。
  4. 如果對 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;
}                                              
           
Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程
使用 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;
}                 
           
Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程

線程分離

pthread_detach 函數

程序當中沒有

實作線程分離

int pthread_detach(pthread_t thread);

成功:0;失敗:錯誤号

  1. 線程分離狀态:指定該狀态,線程主動與主要線程斷開關系。線程結束後,其退出狀态不由其他線程擷取,而 直接自己自動釋放。網絡、多線程伺服器常用。
  2. 程序若有該機制,将不會産生僵屍程序。僵屍程序的産生主要由于程序死後,大部分資源被釋放,一點殘留資 源仍存于系統中,導緻核心認為該程序仍存在。

    也可使用 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;
}
           
Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程

注意事項

一般情況下,線程終止後,其終止狀态一直保留到其它線程調用 pthread_join 擷取它的狀态為止。但是線程也 可以被置為 detach 狀态,這樣的線程一旦終止就立刻回收它占用的所有資源,而不保留終止狀态。**不能對一個已 經處于 detach 狀态的線程調用 pthread_join,這樣的調用将傳回 EINVAL 錯誤。**也就是說,如果已經對一個線程調用 了 pthread_detach 就不能再調用 pthread_join 了。

殺死線程

pthread_cancel 函數

殺死(取消)線程 其作用,對應程序中 kill() 函數。

int pthread_cancel(pthread_t thread);

成功:0;失敗:錯誤号

注意:線程的取消并不是實時的,而有一定的延時。需要等待線程到達某個取消點(檢查點)。

  1. 類似于玩遊戲存檔,必須到達指定的場所(存檔點,如:客棧、倉庫、城裡等)才能存儲進度。殺死線程也不是 立刻就能完成,必須要到達取消點。
  2. 取消點:是線程檢查是否被取消,并按請求進行動作的一個位置。通常是一些系統調用

    creat,open,pause, close,read,write.....

    執行指令

    man 7 pthreads

    可以檢視具備這些取消點的系統調用清單。也可參閱

    APUE.12.7

    取消選項小節。

    可粗略認為一個系統調用(進入核心)即為一個取消點。如線程中沒有取消點,可以通過調用 pthreestcancel 函數 自行設定一個取消點。

  3. 被取消的線程, 退出值定義在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;
}        
           
Linux系統程式設計---14(回收子線程,回收多個子線程,線程分離,殺死線程)回收子線程線程分離殺死線程

當把一個線程殺死後,它的傳回值是-1;

終止線程方式

總結:終止某個線程而不終止整個程序,

有三種方法:

  1. 從線程主函數 return。這種方法對主要線程不适用,從 main 函數 return 相當于調用 exit。
  2. 一個線程可以調用 pthread_cancel 終止同一程序中的另一個線程。
  3. 線程可以調用 pthread_exit 終止自己。

pthread_equal 函數

比較兩個線程 ID 是否相等。

int pthread_equal (pthread_t t1,pthread_t t2);

有可能 Linux 在未來線程

ID pthread_t

類型被修改為結構體實作。

繼續閱讀