pthread_cleanup_push
線程終止時的清理
不論是可預見的線程終止還是異常終止,都會存在資源釋放的問題,在不考慮因運行出錯而退出的前提下,如何保證線程終止時能順利的釋放掉自己所占用的資源,特別是鎖資源,就是一個必須考慮解決的問題。
最經常出現的情形是資源獨占鎖的使用:線程為了訪問臨界資源而為其加上鎖,但在訪問過程中被外界取消,如果線程處于響應取消狀態,且采用異步方式響應,或者在打開獨占鎖以前的運行路徑上存在取消點,則該臨界資源將永遠處于鎖定狀態得不到釋放。外界取消操作是不可預見的,因此的確需要一個機制來簡化用于資源釋放的編程。
在POSIX線程API中提供了一個pthread_cleanup_push()/pthread_cleanup_pop()函數對用于自動釋放資源--從pthread_cleanup_push()的調用點到pthread_cleanup_pop()之間的程序段中的終止動作(包括調用pthread_exit()和取消點終止)都將執行pthread_cleanup_push()所指定的清理函數。API定義如下:
| ? void pthread_cleanup_push(void (*routine) (void *), void *arg)void pthread_cleanup_pop(int execute) |
?
pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的棧結構管理,void routine(void *arg)函數在調用pthread_cleanup_push()時壓入清理函數棧,多次對pthread_cleanup_push()的調用將在清理函數棧中形成一個函數鏈,在執行該函數鏈時按照壓棧的相反順序彈出。execute參數表示執行到pthread_cleanup_pop()時是否在彈出清理函數的同時執行該函數,為0表示不執行,非0為執行;這個參數并不影響異常終止時清理函數的執行。
pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式實現的,這是pthread.h中的宏定義:
| ? #define pthread_cleanup_push(routine,arg) / { struct _pthread_cleanup_buffer _buffer; / _pthread_cleanup_push (&_buffer, (routine), (arg));#define pthread_cleanup_pop(execute) / _pthread_cleanup_pop (&_buffer, (execute)); } |
?
可見,pthread_cleanup_push()帶有一個"{",而pthread_cleanup_pop()帶有一個"}",因此這兩個函數必須成對出現,且必須位于程序的同一級別的代碼段中才能通過編譯。在下面的例子里,當線程在"do some work"中終止時,將主動調用pthread_mutex_unlock(mut),以完成解鎖動作。
| ? pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);pthread_mutex_lock(&mut);/* do some work */pthread_mutex_unlock(&mut);pthread_cleanup_pop(0); |
?
必須要注意的是,如果線程處于PTHREAD_CANCEL_ASYNCHRONOUS狀態,上述代碼段就有可能出錯,因為CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之間發生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之間發生,從而導致清理函數unlock一個并沒有加鎖的mutex變量,造成錯誤。因此,在使用清理函數的時候,都應該暫時設置成PTHREAD_CANCEL_DEFERRED模式。為此,POSIX的Linux實現中還提供了一對不保證可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()擴展函數,功能與以下代碼段相當:
| ? { int oldtype; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); pthread_cleanup_push(routine, arg); ... pthread_cleanup_pop(execute); pthread_setcanceltype(oldtype, NULL); } |
?
?
線程終止的同步及其返回值
一般情況下,進程中各個線程的運行都是相互獨立的,線程的終止并不會通知,也不會影響其他線程,終止的線程所占用的資源也并不會隨著線程的終止而得到釋放。正如進程之間可以用wait()系統調用來同步終止并釋放資源一樣,線程之間也有類似機制,那就是pthread_join()函數。
| ? void pthread_exit(void *retval) int pthread_join(pthread_t th, void **thread_return)int pthread_detach(pthread_t th) |
?
pthread_join()的調用者將掛起并等待th線程終止,retval是pthread_exit()調用者線程(線程ID為th)的返回值,如果thread_return不為NULL,則*thread_return=retval。需要注意的是一個線程僅允許唯一的一個線程使用pthread_join()等待它的終止,并且被等待的線程應該處于可join狀態,即非DETACHED狀態。
如果進程中的某個線程執行了pthread_detach(th),則th線程將處于DETACHED狀態,這使得th線程在結束運行時自行釋放所占用的內存資源,同時也無法由pthread_join()同步,pthread_detach()執行之后,對th請求pthread_join()將返回錯誤。
一個可join的線程所占用的內存僅當有線程對其執行了pthread_join()后才會釋放,因此為了避免內存泄漏,所有線程的終止,要么已設為DETACHED,要么就需要使用pthread_join()來回收。
?
關于pthread_exit()和return
理論上說,pthread_exit()和線程宿體函數退出的功能是相同的,函數結束時會在內部自動調用pthread_exit()來清理線程相關的資源。但實際上二者由于編譯器的處理有很大的不同。
在進程主函數(main())中調用pthread_exit(),只會使主函數所在的線程(可以說是進程的主線程)退出;而如果是return,編譯器將使其調用進程退出的代碼(如_exit()),從而導致進程及其所有線程結束運行。
其次,在線程宿主函數中主動調用return,如果return語句包含在pthread_cleanup_push()/pthread_cleanup_pop()對中,則不會引起清理函數的執行,反而會導致segment fault。
http://blog.csdn.net/newnewman80/article/details/6276788
轉載于:https://www.cnblogs.com/newlist/archive/2012/02/11/2346285.html
總結
以上是生活随笔為你收集整理的pthread_cleanup_push的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【原】display:inline-bl
- 下一篇: 鼠标事件的