線程正常終止pthread_exit,pthread_join,pthread_kill,pthread_cancel,sigwait,sigaddset


 

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

int pthread_detach(pthread_t thread);

void pthread_exit(void *retval);

 

線程正常終止的方法:

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.

示例代碼:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>


void* thread_func(void* arg)
{
    printf("thread:%lu is running\n", pthread_self());
    int rv = 44;
    pthread_exit((void*)rv);
}

int main()
{
    pthread_t thid;
    int rv;

    pthread_create(&thid, NULL, thread_func, NULL);
    
    printf("main thread begin join\n");
    pthread_join(thid, (void*)&rv);
    printf("main thread end join\n");
    printf("the thread:%lu exit:%d\n", thid, rv);

    return 0;
}

 

----------------------------------------------------------------------------

 

將線程的屬性稱為detached,則線程退出時會自己清理資源。

void *start_run( void *arg)
{
//dosomework
}
 
intmain()
{
  pthread_t thread_id;
 
  pthread_attr_   tattr;
 
  pthread_attr_init(&attr);
 
  pthread_attr_setdetachstate(&attr,  PTHREAD_CREATE_DETACHED);
 
  pthread_create(&thread_id,  &attr,  start_run,  NULL);
 
  pthread_attr_destroy( &attr );
 
  sleep(5);
 
  exit (0);
}
 
在線程設置為joinable后,可以調用pthread_detach()使之成為detached。但是相反的操作則不可以。還有,如果線程已經調用pthread_join()后,則再調用pthread_detach()則不會有任何效果。

線程可以通過自身執行結束來結束,也可以通過調用pthread_exit()來結束線程的執行。另外,線程甲可以被線程乙被動結束。這個通過調用pthread_cancel()來達到目的。

當然,線程也不是被動的被別人結束。它可以通過設置自身的屬性來決定如何結束,線程的被動結束分為兩種,一種是異步終結,另外一種是同步終結。異步終結就是當其他線程調用pthread_cancel的時候,

線程就立刻被結束。而同 步終結則不會立刻終結,它會繼續運行,直到到達下一個結束點(cancellation point)。當一個線程被按照默認的創建方式創建,那么它的屬性是同步終結。

 

1 線程取消的定義

一般情況下,線程在其主體函數退出的時候會自動終止,但同時也可以因為接收到另一個線程發來的終止(取消)請求而強制終止。

2 線程取消的語義

1. 線程取消的方法是向目標線程發Cancel信號,但如何處理Cancel信號則由目標線程自己決定,或者忽略(當禁止取消時)、或者立即終止(當在取消點 或異步模式下)、或者繼續運行至Cancelation-point(取消點,下面將描述),總之由不同的Cancelation狀態決定。

2. 線程接收到CANCEL信號的缺省處理(即pthread_create()創建線程的缺省狀態)是繼續運行至取消點再處理(退出),或在異步方式下直接 退出。一個線程處理cancel請求的退出操作相當於pthread_exit(PTHREAD_CANCELED)。當然線程可以通過設置為 PTHREAD_CANCEL_DISABLE來拒絕處理cancel請求,稍后會提及。

3. 線程的取消與線程的工作方式(joinable或detached)無關。

3 取消點

根據POSIX標准,pthread_join()、 pthread_testcancel()、pthread_cond_wait()、 pthread_cond_timedwait()、sem_wait()、sigwait()等函數以及read()、write()等會引起阻塞的系 統調用都是Cancelation-point,而其他pthread函數都不會引起Cancelation動作。但是pthread_cancel的手冊頁聲稱,由於LinuxThread庫與C庫結合得不好,因而目前C庫函數都不是Cancelation-point;但CANCEL信號會使線程從阻塞的系統調用中退出,並置EINTR錯誤碼,因此可以在需要作為Cancelation-point的系統調用前后調用pthread_testcancel(),從而達到POSIX標准所要求的目標,即如下代碼段:   
pthread_testcancel();   
retcode = read(fd, buffer,length);   
pthread_testcancel();

使用前 須判斷線程ID的有效性!即判斷並保證:thrd != 0 否則有可能會出現“段錯誤”的異常!

4 程序設計方面的考慮

1. 如果線程處於無限循環中,且循環體內沒有執行至取消點的必然路徑,則線程無法由外部其他線程的取消請求而終止。因此在這樣的循環體的必經路徑上應該加入pthread_testcancel()調用。

2. 當pthread_cancel()返回時,線程未必已經取消,可能僅僅將請求發送給目標線程,而目標線程目前沒有到達取消點,如果要知道線程在何時中止,就需要在取消它之后調用pthread_join()。有一個例外是當線程被detach后,不能這樣處理:
a) 當join一個已經detached的線程時,返回EINVAL;
b) 如果join后該線程設置為detached,則detach將不起作用。
因此,如果知道一個線程可能會以分離方式運行,就不需要在pthread_cancel()后調用pthread_join()。

5 與線程取消相關的pthread函數

int pthread_cancel(pthread_t thread) 
發送終止信號給thread線程,如果成功則返回0,否則為非0值。發送成功並不意味着thread會終止。

int pthread_setcancelstate(int state, int *oldstate) 
設 置本線程對Cancel信號的反應,state有兩種值:PTHREAD_CANCEL_ENABLE(缺省)和 PTHREAD_CANCEL_DISABLE,分別表示收到信號后設為CANCLED狀態和忽略CANCEL信號繼續運行;old_state如果不為 NULL則存入原來的Cancel狀態以便恢復。

int pthread_setcanceltype(int type, int *oldtype) 
設 置本線程取消動作的執行時機,type有兩種取值:PTHREAD_CANCEL_DEFFERED 和 PTHREAD_CANCEL_ASYCHRONOUS,僅當Cancel狀態為Enable時有效,分別表示收到信號后繼續運行至下一個取消點再退出和 立即執行取消動作(退出);oldtype如果不為NULL則存入運來的取消動作類型值。

void pthread_testcancel(void) 
檢查本線程是否處於Canceld狀態,如果是,則進行取消動作,否則直接返回。

6 檢測一個線程是否還活着的pthread函數

int pthread_kill(pthread_t thread, int sig
向指定ID的線程發送sig信號,如果線程的代碼內不做任何信號處理,則會按照信號默認的行為影響整個進程。也就是說,如果你給一個線程發送了SIGQUIT,但線程卻沒有實現signal處理函數,則整個進程退出。
pthread_kill(threadid, SIGKILL)也一樣,他會殺死整個進程。
如果要獲得正確的行為,就需要在線程內實現signal(SIGKILL,sig_handler)。
所以,如果int sig的參數不是0,那一定要清楚到底要干什么,而且一定要實現線程的信號處理函數,否則,就會影響整個進程。
那么,如果int sig的參數是0呢,這是一個保留信號,一個作用就是用來判斷線程是不是還活着。
我們來看一下pthread_kill的返回值:
線程仍然活着:0
線程已不存在:ESRCH
信號不合法:EINVAL

 

-----------------------------------------------------------------------------------------

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<pthread.h>
#include<time.h>

pthread_t tid;
sigset_t set;

void myfunc()
{
    printf("hello\n:");
}


void* mythread(void* p)
{
    int signum;
    while(1)
    {
        sigwait(&set, &signum);

        if (SIGUSR1 == signum)
        {
            myfunc();
        }
        else if (SIGUSR2 == signum)
        {
            printf("I will sleep 2 seconds and exit\n");
            sleep(2);            
            break;
        }
    }
}



int main()
{
    char tmp;
    void *status;
    sigemptyset(&set);
    sigaddset(&set,SIGUSR1);
    sigaddset(&set,SIGUSR2);
    sigprocmask(SIG_SETMASK,&set,NULL);
    pthread_create(&tid,NULL,mythread,NULL);
    
    
    printf(":");
    while(1)
    {    
        char* p = NULL;
        char str[255];

        
        
        scanf("%c",&tmp);
        p = gets(str);


        //printf("get %c\n", tmp);
        if('a'==tmp)
        {
            pthread_kill(tid,SIGUSR1);//發送SIGUSR1,打印字符串。
            sleep(1);
        }
        else if('q'==tmp)
        {
            //發出SIGUSR2信號,讓線程退出,如果發送SIGKILL,線程將直接退出。
            pthread_kill(tid,SIGUSR2);
            //等待線程tid執行完畢,這里阻塞。
            pthread_join(tid,&status);
            printf("finish\n");
            break;
        }
        else
        {
            printf(":");
        }

 
    }
    return 0;
}

 

 

 

 


免責聲明!

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



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