linux多線程編程

UNIX中,每個線程用一個唯一的ID標識,ID數據類型爲pthread_t。對線程ID進行處理的有以下兩個函數接口:

int pthread_equal(pthread_t tid1, pthread_t tid2); //比較兩個線程ID是否相同

pthread_t pthread_self(void); //獲取本線程的ID


在傳統的UNIX進程模型中,每個進程只有一個控制線程。pthread_create函數可以用來創建一個新線程。

int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void), void *restrict arg);

這裏,無類型的指針arg用來傳參數給啓動例程。


如果進程中任一線程調用了exit,_Exit或者_exit,那麼整個進程就會終止。在不終止整個進程的情況下,單個線程可以

通過以下三種方式推出:

1.線程從啓動例程中返回,返回值是線程的退出碼;

2.線程可以被同一進程中的其他線程取消;

3.線程調用pthread_exit。

void pthread_exit(void *rval_ptr);


進程中其他線程可以利用pthread_join獲取某個線程的退出代碼。如果線程從它的啓動例程中返回,rval_ptr將包含返回碼。

如果線程被取消,由rval_ptr指定的內存單元就置爲PTHREAD_CANCELED。

int pthread_join(pthread_t thread, void **rval_ptr);


需要注意的是,pthread_create和pthread_exit中的無類型參數指針所指向的內存在調用者完成調用後,必須仍然有效,否則

就會出現無效或者非法內存訪問。比如,在調用線程的棧上分配了該結構(比如自動變量),那麼其他的線程在使用這個結

構時內存內容可能已經改變。例如,線程在自己的棧上分配了一個結構然後把指向這個結構的指針傳給pthread_exit,那麼當

調用pthread_join的線程試圖使用該結構時,這個棧可能已經被撤銷,這塊內存也已另做他用。


線程可以通過調用pthread_cancel函數來請求取消同一進程中的其他線程。

int pthread_cancel(pthread_t tid);

在默認情況下,pthread_cancel函數會使得由tid標識的線程的行爲表現爲如同調用了參數爲PTHREAD_CANCELED的

pthread_exit函數。需要注意的是,pthread_cancel並不等待線程終止,它僅僅提出請求。


與進程退出是可調用清理函數類似,線程退出時也可以調用相關清理函數。

void pthread_cleanup_push(void (*rtn) (void), void *arg);

void pthread_cleanup_pop(int execute);



進程原語與線程原語的比較

fork    -----            pthread_create

exit  --------           pthread_exit

waitpid --------     pthread_join

atexit    ---------    pthread_cancel_push

getpid     --------   pthread_self

abort       --------   pthread_cancel                                             


在任何一個時間點上,線程是可結合的(joinable),或者是分離的(detached)。一個可結合的線程能夠被其他線程收回其資源和殺死;在被其他線程回收之前,它的存儲器資源(如棧)是不釋放的。相反,一個分離的線程是不能被其他線程回收或殺死的,它的存儲器資源在它終止時由系統自動釋放。

        線程的分離狀態決定一個線程以什麼樣的方式來終止自己。在默認情況下線程是非分離狀態的,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程纔算終止,才能釋放自己佔用的系統資源。而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。程序員應該根據自己的需要,選擇適當的分離狀態。所以如果我們在創建線程時就知道不需要了解線程的終止狀態,則可以pthread_attr_t結構中的detachstate線程屬性,讓線程以分離狀態啓動。

設置線程分離狀態的函數爲pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二個參數可選爲PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)。這裏要注意的一點是,如果設置一個線程爲分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以後就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以採取一定的同步措施,最簡單的方法之一是可以在被創建的線程裏調用pthread_cond_timewait函數,讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程裏常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,並不能解決線程同步的問題。

另外一個可能常用的屬性是線程的優先級,它存放在結構sched_param中。用函數pthread_attr_getschedparam和函數pthread_attr_setschedparam進行存放,一般說來,我們總是先取優先級,對取得的值修改後再存放回去。

線程等待——正確處理線程終止

#include <pthread.h>

void pthread_exit(void *retval);

void pthread_join(pthread_t th,void *thread_return);//掛起等待th結束,*thread_return=retval;

int pthread_detach(pthread_t th);

如果線程處於joinable狀態,則只能只能被創建他的線程等待終止。

在Linux平臺默認情況下,雖然各個線程之間是相互獨立的,一個線程的終止不會去通知或影響其他的線程。但是已經終止的線程的資源並不會隨着線程的終止而得到釋放,我們需要調用 pthread_join() 來獲得另一個線程的終止狀態並且釋放該線程所佔的資源。(說明:線程處於joinable狀態下)

調用該函數的線程將掛起,等待 th 所表示的線程的結束。 thread_return 是指向線程 th 返回值的指針。需要注意的是 th 所表示的線程必須是 joinable 的,即處於非 detached(遊離)狀態;並且只可以有唯一的一個線程對 th 調用 pthread_join() 。如果 th 處於 detached 狀態,那麼對 th 的 pthread_join() 調用將返回錯誤。

如果不關心一個線程的結束狀態,那麼也可以將一個線程設置爲 detached 狀態,從而讓操作系統在該線程結束時來回收它所佔的資源。將一個線程設置爲detached 狀態可以通過兩種方式來實現。一種是調用 pthread_detach() 函數,可以將線程 th 設置爲 detached 狀態。另一種方法是在創建線程時就將它設置爲 detached 狀態,首先初始化一個線程屬性變量,然後將其設置爲 detached 狀態,最後將它作爲參數傳入線程創建函數 pthread_create(),這樣所創建出來的線程就直接處於 detached 狀態。

創建 detach 線程:

pthread_t tid;

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_create(&tid, &attr, THREAD_FUNCTION, arg);

總之爲了在使用 pthread 時避免線程的資源在線程結束時不能得到正確釋放,從而避免產生潛在的內存泄漏問題,在對待線程結束時,要確保該線程處於 detached 狀態,否着就需要調用 pthread_join() 函數來對其進行資源回收。


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