1. 程式人生 > >pthread中將處理程序送到堆棧上

pthread中將處理程序送到堆棧上

釋放資源 clean handle AC som accep 繼續 參數 mut

pthread_cleanup_push 語法

請使用 pthread_cleanup_push() 將清理處理程序推送到清理棧 (LIFO)。

void pthread_cleanup_push(void(*routine)(void *), void *args);
#include <pthread.h>

/* push the handler "routine" on cleanup stack */

pthread_cleanup_push (routine, arg); 

pthread_cleanup_push 返回值

pthread_cleanup_push() 沒有返回值。

void pthread_cleanup_pop(int execute);
#include <pthread.h>



/* pop the "func" out of cleanup stack and execute "func" */

pthread_cleanup_pop (1);



/* pop the "func" and DONT execute "func" */

pthread_cleanup_pop (0); 

如果彈出函數中的參數為非零值,則會從棧中刪除該處理程序並執行該處理程序。如果該參數為零,則會彈出該處理程序,而不執行它。

線程顯式或隱式調用 pthread_exit(3C) 時,或線程接受取消請求時,會使用非零參數有效地調用 pthread_cleanup_pop()

pthread_cleanup_pop 語法

void pthread_cleanup_pop(int execute);
#include <pthread.h>



/* pop the "func" out of cleanup stack and execute "func" */

pthread_cleanup_pop (1);



/* pop the "func" and DONT execute "func" */

pthread_cleanup_pop (0); 

如果彈出函數中的參數為非零值,則會從棧中刪除該處理程序並執行該處理程序。如果該參數為零,則會彈出該處理程序,而不執行它。

線程顯式或隱式調用 pthread_exit(3C) 時,或線程接受取消請求時,會使用非零參數有效地調用 pthread_cleanup_pop()

pthread_cleanup_pop 返回值

pthread_cleanup_pop() 沒有返回值。

解釋:

首先你必須知道pthread_cleanup_push與pthread_cleanup_pop的目的(作用)是什麽。

比如thread1:
執行
pthread_mutex_lock(&mutex);

//一些會阻塞程序運行的調用,比如套接字的accept,等待客戶連接
sock = accept(......); //這裏是隨便找的一個可以阻塞的接口

pthread_mutex_unlock(&mutex);
這個例子中,如果線程1執行accept時,線程會阻塞(也就是等在那裏,有客戶端連接的時候才返回,或則出現其他故障),線程等待中......

這時候線程2發現線程1等了很久,不賴煩了,他想關掉線程1,於是調用pthread_cancel()或者類似函數,請求線程1立即退出。

這時候線程1仍然在accept等待中,當它收到線程2的cancel信號後,就會從accept中退出,然後終止線程,註意這個時候線程1還沒有執行:
pthread_mutex_unlock(&mutex);
也就是說鎖資源沒有釋放,這回造成其他線程的死鎖問題。

所以必須在線程接收到cancel後用一種方法來保證異常退出(也就是線程沒達到終點)時可以做清理工作(主要是解鎖方面),pthread_cleanup_push與pthread_cleanup_pop就是這樣的。

pthread_cleanup_push(some_clean_func,...)
pthread_mutex_lock(&mutex);

//一些會阻塞程序運行的調用,比如套接字的accept,等待客戶連接
sock = accept(......); //這裏是隨便找的一個可以阻塞的接口

pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0);
return NULL;
上面的代碼,如果accept被cancel後線程退出,會自動調用some_clean_func函數,在這個函數中你可以釋放鎖資源。如果accept沒有被cancel,那麽線程繼續執行,當pthread_mutex_unlock(&mutex);表示線程自己正確的釋放資源了,而執行pthread_cleanup_pop(0);也就是取消掉前面的some_clean_func函數。接著return線程就正確的結束了。

不曉得你明白沒,通俗點就是:
pthread_cleanup_push註冊一個回調函數,如果你的線程在對應的pthread_cleanup_pop之前異常退出(return是正常退出,其他是異常),那麽系統就會執行這個回調函數(回調函數要做什麽你自己決定)。但是如果在pthread_cleanup_pop之前沒有異常退出,pthread_cleanup_pop就把對應的回調函數取消了,

關於取消點的解釋:

比如你執行:
printf("thread sleep\n");
sleep(10);
printf("thread wake...\n");
在sleep函數中,線程睡眠,結果收到cancel信號,這時候線程從sleep中醒來,但是線程不會立刻退出。這是應為pthread與C庫方面的原因(具體是啥我也不清楚),pthread的建議是,如果一個函數是阻塞的,那麽你必須在這個函數前後建立取消點,比如:
printf("thread sleep\n");
pthread_testcancel();
sleep(10);
pthread_testcancel();
printf("thread wake...\n");
這樣,就添加了兩個取消掉。在執行到pthread_testcancel的位置時,線程才可能響應cancel退出進程。

額外的知識:
對於cancel信號,線程有兩種方法: 忽略,和響應。默認是響應
接收到cancel信號,線程有兩種處理類型: 立即響應 和 延遲響應(在最近的取消點響應),默認是延遲響應

pthread中將處理程序送到堆棧上