linux高級編程之線程間的通信(pthread_cleanup_push和pthread_cleanup_pop)

linux高級編程之線程間的通信(pthread_cleanup_push和pthread_cleanup_pop)

 

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

      pthread_cleanup_pushpthread_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時     

     關於書上有句原話:“如果execute參數置爲0,清理函數將不被調用”,我覺得說的有問題,而且接下來我摘抄了書上的一個例子,剛好驗證了他說的這句話的錯誤,而且我也驗證了下,當然在一篇博客中我看到這樣的解釋覺得很合理:當pthread_cleanup_pop()函數的參數爲0時,僅僅在線程調用pthread_exit函數或者其它線程對本線程調用 pthread_cancel函數時,纔在彈出“清理函數”的同時執行該“清理函數”。同樣我也取了博客中的第二個例子來說明pthread_cancel調用時,pthread_cleanup_push會用清理函數。(ps:重新看了下這個疑問,翻書看了下,其實後面還有句話:“不管上述哪種情況,pthread_cleanup_pop都將刪除上次pthread_cleanup_push調用建立的清理函數”,所以可能作者想表達的是設置爲pop參數設置爲0,在其它兩種情況下同樣會被調用)

   參考博客:http://blog.chinaunix.net/uid-26772137-id-3369725.html

例1:

#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。

例2:

#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中的語句先被打印。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章