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;