【Posix線程】pthread_clean_push和pthread_clean_up的使用

<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

 

 
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.

<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

 
 
另附一個測試程序:
 
 
 

#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()
*/

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