轉自:http://blog.chinaunix.net/u2/72995/showart_1218514.html void pthread_cleanup_push(void (*routine)(void*), void *arg);
void pthread_cleanup_pop(int execute);//這裡的int參數,0是不執行push的內容,非0是執行。
原型很簡單,功能跟atexit()差不多,只不過一個是線程一個是進程。
用來設定在push/pop內線程退出時要做的事情。
需要注意的問題有幾點:
1,push與pop一定是成對出現的,其實push中包含"{"而pop中包含"}",少一個不行。
2,push可以有多個,同樣的pop也要對應的數量,遵循"先進後出原則"。
push進去的函數可能在以下三個時機執行:
1,顯示的調用pthread_exit();
或
2,在cancel點線程被cancel。
或
3,pthread_cleanup_pop()的參數不為0時。
以上動作都限定在push/pop涵蓋的代碼內。
前面的2個比較好理解,關鍵是pthread_cleanup_pop參數問題,其實int那是因為c沒有bool,這裡的參數只有0與非0的區別,對pthread_cleanup_pop,參數是5和10都是一樣的,都是非0。
我們經常會看到這樣的代碼:
void child(void *t)
{
pthread_cleanup_push(pthread_mutex_unlock,&mutex);
pthread_mutex_lock(&mutex);
..............
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
}
為啥pthread_cleanup_pop是0呢,他根本就不會執行push進來的函數指標指向的函數,沒錯,是不執行,真要執行了就麻煩了。
那為啥還得寫這句呢?
那是因為push和pop是必須成對出現的,不寫就是語法錯誤。
這麼寫的目的主要是為了保證mutex一定可以被unlock,因為,在pthread_mutex_lock和pthread_mutex_unlock之間可能發生任何事情,比如,存在N個cancel點導致線程被main或者其他線程給cancel,而cancel動作是不會釋放互斥鎖的,這樣就鎖死啦。
通過pthread_cleanup_push加入一個函數pthread_mutex_unlock,參照上面執行時機的說明,線上程被cancel的時候,就可以作釋放鎖的清理動作。如果線程正常執行,知道運行到pthread_cleanup_pop,那麼鎖已經被中間代碼裡的pthread_mutex_unlock給釋放了,這時候如果pthread_cleanup_pop的參數不為0,則會再次釋放,錯就錯在這裡了,釋放了兩次。
所以,pthread_cleanup_pop(0)是必須的,因為,首先要成對出現,其次,我們不希望他真的執行到這裡釋放兩次。
同樣道理:
void *exit1(void *t)
{
printf("exit1/n");
}
void *child(void *t)
{
pthread_cleanup_push(exit1,NULL);
.....
pthread_exit(NULL);
pthread_cleanup_pop(0);
}
exit1函數是在pthread_exit(NULL)的時候執行的,pthread_cleanup_pop的參數是不是0沒有關係,因為根本執行不到這裡。
而換成這樣:
pthread_cleanup_push(exit1,NULL);
......
pthread_cleanup_pop(0);
pthread_exit(NULL);
則exit1不會執行,因為pop的參數是0,如果把pop的參數修改為1則會執行exit1,那會執行兩次嗎?NO,因為pthread_exit在push/pop block的外面,他不會觸發exit1.
pthread_cleanup_push(exit1,NULL);
pthread_cleanup_push(exit2,NULL);
........
pthread_cleanup_pop(0);
pthread_cleanup_pop(1);
那0和1分別控制的是誰?配對原則,從外到裡一對一對的拔掉就可以了,顯然,0控制的是exit2.
另附一個測試程式:
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h>
void clean ( void * p ) { printf ( "clean()/n" ); }
void * thread ( void * p ) {
pthread_cleanup_push( clean, NULL); printf ( "thread sleep/n" ); sleep (10); printf("thread wake.../n"); pthread_cleanup_pop(1); }
int main () { pthread_t id; pthread_mutexattr_t attr; pthread_create ( &id, NULL, thread, NULL ); printf ( "main sleep/n"); sleep(3); printf ( "cancel thread:%u/n", id ); pthread_cancel ( id ); return 0; }
/* 輸出: thread sleep main sleep cancel thread:1082399936 clean() */
|