控制清理函數的函數有兩個,一個是 pthread_cleanup_push(), 用來把清理函數壓入棧中,另外一個是 pthread_cleanup_pop(), 用來把棧中的函數彈出來。linux
用這兩個函數組合,能夠達到在線程退出時,清理線程數據的做用, 例如對 mutex 進行解鎖等。數組
下面是這兩個函數的函數原型:多線程
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute); //Compile and link with -pthread.
咱們先寫個簡單的例子,感性認識一下這兩個函數的做用:函數
#include <stdio.h> #include <pthread.h> void handlers(void *arg) { if(NULL != arg) { printf("%s() : [%s]\n", __func__, (char*)arg); } else { printf("%s()\n", __func__); } } void * thread_start(void *arg) { pthread_cleanup_push(handlers, "one"); pthread_cleanup_push(handlers, "two"); pthread_cleanup_push(handlers, "thr"); printf("This is thread [%u]\n", (unsigned int)pthread_self()); pthread_exit("he~he~"); //do something pthread_cleanup_pop(1); pthread_cleanup_pop(1); pthread_cleanup_pop(1); return NULL; } int main() { pthread_t pt; pthread_create(&pt, NULL, thread_start, NULL); void *r = NULL; pthread_join(pt, &r); if(NULL != r) { printf("thread return : [%s]\n", (const char*)r); } return 0; }
編譯並運行:ui
This is thread [3290769152] handlers() : [thr] handlers() : [two] handlers() : [one] thread return : [he~he~]
咱們在代碼裏面是按照 one、two、thr 的順序調用的 pthread_cleanup_push() 函數, 結果在運行後獲得的結果中,卻看到它們輸出的順序正好倒過來了。 這正是這對函數的性質。spa
而且這對函數還有一個性質,那就是使用 pthread_cleanup_push() 和 pthread_cleanup_pop() 之間使用 return 的話,會致使以後的 pthread_cleanup_pop() 不起做用。 這是爲何呢?緣由是,其實 pthread_cleanup_push() 和 pthread_cleanup_pop() 不是函數, 而是一對宏。線程
其宏定義在頭文件 pthread.h 中能夠看到,宏定義以下:code
# define pthread_cleanup_push(routine, arg) \ do { \ __pthread_cleanup_class __clframe (routine, arg) # define pthread_cleanup_pop(execute) \ __clframe.__setdoit (execute); \ } while (0)
咱們寫個更簡單的程序,把這兩個宏展開後看一看是什麼樣結果:blog
代碼以下:get
# define pthread_cleanup_push(routine, arg) \ do { \ __pthread_cleanup_class __clframe (routine, arg) # define pthread_cleanup_pop(execute) \ __clframe.__setdoit (execute); \ } while (0)
編譯:
gcc -g -E -o pthread_cleanup_macro.i pthread_cleanup_macro.c
查看 pthread_cleanup_macro.i 的代碼:
void hand(void* arg) { printf("do nothing"); } void *thread_start(void* arg) { do { __pthread_unwind_buf_t __cancel_buf; void (*__cancel_routine) (void *) = (hand); void *__cancel_arg = ("a"); int __not_first_call = __sigsetjmp ((struct __jmp_buf_tag *) (void *) __cancel_buf.__cancel_jmp_buf, 0); if (__builtin_expect((__not_first_call), 0)) { __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf); } __pthread_register_cancel (&__cancel_buf); do {; printf("This is thread [%u]\n", (unsigned int)pthread_self()); do { } while (0); } while (0); __pthread_unregister_cancel (&__cancel_buf); if (1) __cancel_routine (__cancel_arg); } while (0); return ((void *)0); } int main() { return 0; }
能夠看到,thread_start 函數裏面的 pthread_cleanup_push() 和 pthread_cleanup_pop() 已經被展開了。咱們把 thread_start 函數裏面的代碼再修飾一下格式,結果以下:
void *thread_start(void* arg) { do { __pthread_unwind_buf_t __cancel_buf; void (*__cancel_routine) (void *) = (hand); void *__cancel_arg = ("a"); int __not_first_call = __sigsetjmp ((struct __jmp_buf_tag *) (void *) __cancel_buf.__cancel_jmp_buf, 0); if (__builtin_expect((__not_first_call), 0)) { __cancel_routine (__cancel_arg); __pthread_unwind_next (&__cancel_buf); } __pthread_register_cancel (&__cancel_buf); do { ; printf("This is thread [%u]\n", (unsigned int)pthread_self()); do { } while (0); } while (0); __pthread_unregister_cancel (&__cancel_buf); if (1) __cancel_routine (__cancel_arg); } while (0); return ((void *)0); }
能夠看到,咱們輸出線程信息的 printf 語句,被一層層的 do{}while(0) 給包圍了。 若是在 pthread_cleanup_push() 和 pthread_cleanup_pop() 之間加一個 return , 那麼整個 do{}while(0) 就會被跳出,後面的代碼確定也就不會被執行了。
同步地址:https://www.fengbohello.top/archives/linux-pthread-cleanup