線程相關及需要注意的地方

1.創建thread.

int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr,
              void *(*start_routine)(void*), void *restrict arg);

 

參數1:pthread_t *restrict thread:創建thread的thread ID.

參數2:const pthread_attr_t *restrict attr:創建線程的屬性。

參數3:void *(*start_routine)(void*):thread服務程序。

參數4:void *restrict arg:thread服務程序參數。

 

2. 等待目標線程終止:

pthread_join() 函數會一直阻塞調用線程,直到指定的線程終止。

指定的線程必須位於當前的進程中,而且不得是分離線程。所有創建時屬性爲PTHREAD_CREATE_JOINABLE的非分離thread. 最終都需要調用pthread_join() or pthread_detach() 。這樣thread所佔資源和 Thread ID 才被釋放。

 

3. 分離thread:

int pthread_detach(pthread_t thread);

pthread_detach()指出當thread 結束時,thread所佔資源和Thread ID會被釋放和再利用。如果調用pthread_detach()時,thread沒有結束,它並不會導致thread退出。它只對PTHREAD_CREATE_JOINABLE 非分離thread有效。

 

4. 獲取thread ID:

pthread_t pthread_self(void);

返回調用thread的thread ID.

 

5. 比較thread ID:

int pthread_equal(pthread_t t1, pthread_t t2);如果 tid1 和 tid2 相等,pthread_equal() 將返回非零值,否則將返回

 

6. 向thread發信號:

int pthread_kill(pthread_t thread, int sig);

tid 所指定的線程必須與調用線程在同一個進程中。sig 參數必須來自 signal(5) 提供的列表。

 

7. 退出線程:

void pthread_exit(void *value_ptr);

pthread_exit()用來終止調用thread並置位value_ptr.這個值會交給pthread_join。

 

 

Thread的取消:

同一進程內,某個Thread可以向其它thread發送取消要求,要求目標thread退出運行。

取消請求的處理方式取決於目標線程的狀態。狀態由以下兩個函數確定:pthread_setcancelstate() 和pthread_setcanceltype()

 pthread_setcancelstate() 啓用或禁用線程取消功能。創建線程時,缺省情況下線程取消功能處於啓用狀態。

pthread_setcanceltype() 可以將取消類型設置爲延遲或異步模式。創建線程時,缺省情況下會將取消類型設置爲延遲模式。在延遲模式下,只能在取消點取消線程。在異步模式下,可以在執行過程中的任意一點取消線程。因此建議不使用異步模式。

 

執行取消操作存在一定的危險。大多數危險都與完全恢復不變量和釋放共享資源有關。取消線程時一定要格外小心,否則可能會使互斥保留爲鎖定狀態,從而導致死鎖。或者,已取消的線程可能保留已分配的內存區域,但是系統無法識別這一部分內存,從而無法釋放它。

 

如果創建thread時使用缺省設置,則thread可以被取消,併爲異步方式,所以向某一thread發送pthread_cancel()後,並不保證什麼時候目標thread會被取消。只有當目標thread運行至取消點時纔會真正退出。

 

類似Read,write等阻塞函數可以被看作取消點,但Sam記得並不能保證。所以建議使用手動添加取消點

pthread_testcancel();

當線程取消功能處於啓用狀態且取消類型設置爲延遲模式時,pthread_testcancel() 函數有效。如果在取消功能處於禁用狀態下調用 pthread_testcancel(),則該函數不起作用。

請務必僅在線程取消操作安全的序列中插入 pthread_testcancel()

例如:Sam一直以爲poll()函數這樣的阻賽類函數爲cancel點,但其實不是,需要手動添加cancel點。

 

 

 

 幾個誤區及注意點:

 誤區1: 分離線程不能被cancel.

這是將pthread_join與pthread_cancel搞混了。

thread分離可以在創建時設定,也可以用pthread_detach()在創建後設定。

被設定成分離線程後,表明它在退出thread時會自動回收資源。所以不需要pthread_join. 但分離thread完全可以接收pthread_cancel()來退出。

 

誤區2:已經退出的thread,再去對它pthread_cancel()會出錯。

不會出錯,如果某thread已經退出,再向它發送pthread_cancel().不會出錯。但會返回ESRCH。此值爲3。

 ESRCH  No thread could be found corresponding to that specified by the given thread ID.

 

這裏顯示出:一個thread,不管自身return或pthread_exit(). 此thread都算停掉了。只是不分離thread需要使用pthread_join來回收資源而已。

 

 

注意點1:不管是否分離,主thread先於其它thread退出,都是不可控的。也就是說會不可預知錯誤。

所以,主thread不要使用return,exit等退出。 而是使用pthread_exit().

主thread使用pthread_exit(). 則會阻賽之,直到所有子thread退出後才退出。

 

 

推薦的做法:

常常有這樣的需求,一個子thread既需要在某些事件發生時自己退出,也可能被主thread要求退出。

則可以做如下設計:

子thread自己退出時,使用pthread_exit().

其它thread要求它退出時,是用pthread_cancel(). pthread_join().

 

則當其它thread先要求它退出時,走正常途徑,pthread_cancel()導致其退出。pthread_join()確保其退出並回收資源。

當其自動使用pthread_exit()退出時,最終主thread也會調用pthread_cancel(),則返回錯誤。但pthread_join()則確保回收資源。

 

 

pthread系列函數錯誤碼:

大多數系統函數執行正確返回0。否則返回-1。錯誤碼在errno中。所以可以使用perror()來顯示錯誤。

但pthread系列函數卻通過返回值傳遞error code. 並不向errno中寫入錯誤碼。所以不能使用perror()來查看錯誤原因。

 

可以使用strerror(pthread_rel) 來打印錯誤原因:

 

iRel_pthread = pthread_create(&mRtid, NULL, thread_Read_Data, this);
if(iRel_pthread != 0)
{
  std::cout <<"kDriver: Create Scan_Device thread Error." << strerror(iRel_pthread)  <<std::endl;
 }

 

發佈了13 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章