1. 程式人生 > 其它 >linux執行緒間的通訊(pthread_cleanup_push和pthread_cleanup_pop,pthread_join,pthread_detach)

linux執行緒間的通訊(pthread_cleanup_push和pthread_cleanup_pop,pthread_join,pthread_detach)

執行緒可以安排他退出時需要呼叫的函式,這與程序可以用atexit函式安排程序退出時需要呼叫的函式是類似的。這樣的函式稱為執行緒清理處理程式,執行緒可以建立多個清理處理程式。處理程式記錄在棧中,也就是說他們的執行順序與他們註冊的順序相反。

pthread_cleanup_push和pthread_cleanup_pop函式原型如下:

標頭檔案:#include <pthread.h>

函式原型:void pthread_cleanup_push(void (*rtn)(void *), void *arg);

void pthread_clean_pop(int execute);

void(*rtn)(void *):執行緒清理函式

另外簡單記錄下pthread_cancel函式。該函式為執行緒取消函式,用來取消同一程序中的其他程序,函式原型:

標頭檔案:#include <pthread.h>

函式原型:pthread_cancel(pthread_t tid);

tid:執行緒id

當執行緒執行以下動作時,呼叫清理函式,呼叫的引數為arg,清理函式rtn的呼叫順序是由pthread_cleanup_push函式來安排的。

●呼叫pthread_exit時

●響應取消請求時

●用非零execute引數呼叫pthread_cleanup_pop時 (??)

當pthread_cleanup_pop()函式的引數為0時,僅僅線上程呼叫pthread_exit函式或者其它執行緒對本執行緒呼叫pthread_cancel函式時,才在彈出“清理函式”的同時執行該“清理函式”。

同樣我也取了部落格中的第二個例子來說明pthread_cancel呼叫時,pthread_cleanup_push會用清理函式。

(ps:“不管上述哪種情況,pthread_cleanup_pop都將刪除上次pthread_cleanup_push呼叫建立的清理函式”,所以可能作者想表達的是設定為pop引數設定為0,在其它兩種情況下同樣會被呼叫)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
 
void cleanup(void *arg)
{
    printf("cleanup:%s\n",(char*)arg);
}
void *thr_fn1(void *arg)
{
    printf("thread 1 start\n");
    pthread_cleanup_push(cleanup,"thread 1 first handler");
    pthread_cleanup_push(cleanup,"thread 1 second handler");
    printf("thread 1 push complete\n");
    if(arg)
        return ((void *)1);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return ((void *)1);
}
void *thr_fn2(void *arg)
{   
    printf("thread 2 start\n");
    pthread_cleanup_push(cleanup,"thread 2 first handler");
    pthread_cleanup_push(cleanup,"thread 2 second handler");
    printf("thread 2 push complete\n");
    if(arg)
        pthread_exit((void *)2);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_exit((void *)2);
}
int main()
{
    int err;
    pthread_t tid1,tid2;
    void *tret;
    err = pthread_create(&tid1,NULL,thr_fn1,(void *)1);
    if(err != 0)
    {
        fprintf(stderr,"thread create 1 is error\n");
        return -1;
    }
    err = pthread_create(&tid2,NULL,thr_fn2,(void *)1);
    if(err != 0)
    {
        fprintf(stderr,"thread create 2 is error\n");
        return -2;
    }
    err = pthread_join(tid1,&tret);
    if(err != 0)
    {
        fprintf(stderr,"can't join with thread 1\n");
        return -2;
    }
 
    //pthread_cancel(tid1);
    printf("thread 1 exit code %d\n",tret);
    err = pthread_join(tid2,&tret);
    if(err != 0)
    {
        fprintf(stderr,"can't join with thread 2\n");
        return -2;
    }
    printf("thread 2 exit code %d\n",tret);
    return 0;
}

執行結果如下:

從輸出結果可以看出:兩個執行緒都呼叫了,但是卻只調用了第二個執行緒的清理處理程式,所以如果執行緒是通過從它的啟動歷程中返回而終止的話,那麼它的清理處理程式就不會被呼叫,

還要注意清理程式是按照與它們安裝時相反的順序被呼叫的。從程式碼輸出也可以看到先執行的thread 2 second handler後執行的thread 2 first handler。

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
void clean_fun1(void * arg)
{
    printf("this is clean fun1\n");
}
void clean_fun2(void * arg)
{
    printf("this is clean fun2\n");
}
void * thread_fun(void * arg)
{
    pthread_cleanup_push(clean_fun1,NULL);
    pthread_cleanup_push(clean_fun2,NULL);
    sleep(100);
    //這裡要注意,如果將sleep(100);換成while(1);的話,程式會一直暫停.push和pop要成對出現.
    //因為while(1);執行的太快,執行緒不接受cancel訊號
    //while(1);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return NULL;
}
int main()
{
    pthread_t tid1;
    int err;
    err=pthread_create(&tid1,NULL,thread_fun,NULL);
    if(err!=0)
    {
        perror("pthread_create");
        exit(0);
    }
    sleep(3);
    //printf("test\n");
    err=pthread_cancel(tid1);
    if(err!=0)
    {
        perror("cancel error:");
        exit(0);
    }
    err=pthread_join(tid1,NULL);
    if(err!=0)
    {
        perror("pthread_join error:");
        exit(0);
    }
 
    return 0;
}

執行結果如下:

從上面也可以看出,當呼叫pthread_cancel函式請求後,等到響應請求時,程式碼呼叫了pthread_clean_push函式中的clean_fun1和clean_fun2,函式clean_fun2中的語句先被列印。

pthread_join()與pthread_detach()詳解

1.linux執行緒執行和windows不同,pthread有兩種狀態joinable狀態和unjoinable狀態,

如果執行緒是joinable狀態,當執行緒函式自己返回退出時或pthread_exit時都不會釋放執行緒所佔用堆疊和執行緒描述符(總計8K多)。只有當你呼叫了pthread_join之後這些資源才會被釋放。

若是unjoinable狀態的執行緒,這些資源線上程函式退出時或pthread_exit時自動會被釋放。

2.unjoinable屬性可以在pthread_create時指定,或線上程建立後線上程中pthread_detach自己, 如:pthread_detach(pthread_self()),將狀態改為unjoinable狀態,確保資源的釋放。或者將執行緒置為 joinable,然後適時呼叫pthread_join.

3.其實簡單的說就是線上程函式頭加上 pthread_detach(pthread_self())的話,執行緒狀態改變,在函式尾部直接 pthread_exit執行緒就會自動退出。省去了給執行緒擦屁股的麻煩。

/pthread_detach(pthread_self());
//使執行緒分離出來。當這個執行緒執行完成任務後釋放釋放資源。不然它會保留退出狀態,等待別人來取。
pthread_detach(threadid)和pthread_detach(pthread_self())沒有什麼區別吧!有很嚴格的區別嗎???如果非要講區別不可,我覺得應該是呼叫他們的執行緒不同。
pthread_detach(threadid)函式的功能是使執行緒ID為threadid的執行緒處於分離狀態,一旦執行緒處於分離狀態,該執行緒終止時底 層資源立即被回收;否則終止子執行緒的狀態會一直儲存(佔用系統資源)直到主執行緒呼叫pthread_join(threadid,NULL)獲取執行緒的退 出狀態。通常是主執行緒使用pthread_create()建立子執行緒以後,一般可以呼叫pthread_detach(threadid)分離剛剛建立的子執行緒,這裡的threadid是指子執行緒的threadid;如此以來,該子執行緒止時底層資源立即被回收;被建立的子執行緒也可以自己分離自己,子執行緒呼叫pthread_detach(pthread_self())就是分離自己,因為pthread_self()這個函式返回的就是自己本身的執行緒ID;