linux高級編程之線程間的通信(pthread_cleanup_push和pthread_cleanup_pop)
線程可以安排他退出時需要調用的函數,這與進程可以用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時
關於書上有句原話:“如果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中的語句先被打印。