APUE 4 - 線程 : 概念

對傳統的UNIX進程來說,一個進程中只有一個線程,這就意味着一個進程在同一時刻只能作一件事(即便是多核CPU)。使用多線程技術, 咱們能夠設計程序使得一個進程在同一時刻作多件事。使用多線程編程具備如下優點:node

  1. 咱們能夠以獨立線程分別處理其對應事件類型事件的方式來處理異步事件,以此來簡化咱們的編碼。每一個線程中就能夠以同步編程(順序編程)的方式來編程了。同步(順序)編程要比異步編程簡單的多。
  2. 多進程程序必須使用操做系統提供的複雜機制來實現共享內存、共享文件描述符等,而相比而言,同一進程中的多線程在這個進程中具備相同的內存地址空間和文件描述符。
  3. 一些問題能夠被分割開來以提升程序總體的性能。單線程程序在處理多任務時只能將任務序列化處理,由於它同一時刻只有一個線程在工做。使用多線程控件時,能夠經過一個獨立線程一個任務的方式來交叉處理多個獨立任務。
  4. 一樣的,對於交互式程序來講, 使用多線程技術能夠大大提升程序的反應速度。

一個線程由一個進程中那些能表明當前執行上下文的所必要的信息組成。它包括一個用於標誌線程的線程ID、一個寄存器值的集合、一個堆棧、一個優先級表、一個信號掩碼、一個errno變量和一個指定線程數據(thread-specific data)。進程中的全部資源都被這個進程中的全部線程所共享,它包括程序執行上下文、程序的全局和堆內存、堆棧、文件描述符 。咱們接下來要引述的線程接口來自 POSIX.1-2001。編程

 

 

線程ID

正如每一個進程都有一個進程ID同樣,每一個線程都有一個線程ID,只不過一個進程的進程ID在系統中全局惟一,而線程ID只在線程所屬的進程中有意義。線程ID使用數據類型pthread_t來表示。實現被容許使用結構體來表明pthread_t類型,所以好的實現不該將pthread_t當成整數來對待。所以,咱們必須使用函數來比較兩個線程ID:多線程

1 #include <pthread.h>
2 
3 /* Return:nozero if equal, 0 otherwise */
4 int pthread_equal(pthread_t tid1, pthread_t tid2);

Linux3.2.0使用用無符號長整型實現pthread_t。Solaris 10 使用無符號整形表明pthread_t。FreeBSD 8.0和Mac OS X 10.6.8 使用執行pthread結構體的指針來表明pthread_t。                                                                                                                                          異步

獲取線程ID異步編程

1 #include <pthread.h>
2 
3 /* Return:the thread ID of the calling thread */
4 pthread_t pthread_self(void);

 

 

建立線程

使用pthread,在程序啓動的時候一個進程也是僅有一個線程的,在程序運行的時候他與傳統的進程沒有什麼區別, 直到他在進程中建立了更多的多線程。函數

1 #include <pthread.h>
2 
3 /* Return: 0 if ok, error number on failure */
4 int pthread_create(
5     pthread_t* restrict tidp,
6     const pthread_attr_t* restrict attr,
7     void* (* start_rtn)(void*),
8     void* restrict arg);

tidp 用於獲取線程成功建立後的線程Id;attr用戶自定各類線程屬性;start_rtn指定線程要執行的函數地址;arg爲start_rtn指向函數的參數;性能

新建立的線程能夠訪問進程地址空間並繼承調用線程的浮點環境(floating-point environment)和信號掩碼,然而新線程的阻塞信號集是被清空的。注意pthread類函數在失敗時一般返回一個錯誤碼而不像其餘POSIX函數那樣設置errno。每一個線程擁有一個errno副本僅僅是爲了與現有使用errno的函數兼容。編碼

 

 

終止線程

若是一個進程中任何一個線程調用了 exit、_exit或_Exit,那麼整個進程會被停止。一樣的,向一個線程一個默認處理方式是終止進程的信號會停止這個線程所在的進程。spa

單個線程能夠有如下三種退出方式:操作系統

  1. 簡單的從啓動例程中返回。返回值就是線程的退出碼。
  2. 線程能夠被所屬進程中的另外一個線程取消掉。
  3. 線程能夠經過調用pthread_exit退出
 1 #include <pthread.h>
 2 
 3 /* 
 4     終止線程並經過rval_ptr返回一個值,
 5     rval_ptr能夠被同一進程中調用pthread_join
 6     方法的線程獲取到 
 7 */
 8 void pthread_exit(void* rval_ptr);
 9 
10 /*
11     等待thread線程結束,thread必須是joinable的。
12     若是rval_ptr不爲空,它會複製目標線程的退出碼(
13     如目標線程在pthread_exit中提供的值)到rval_ptr
14     指向的位置。若是目標線程被取消PTHREAD_CANCELED
15     會被放置到rval_ptr指向的位置
16 */
17 void pthread_join(pthread_t thread, void** rval_ptr);    

 傳遞給pthread_exit 和 pthread_create的無類型指針可用於傳輸複雜類型數據。經過pthread_jion方法咱們能夠將咱們等待的線程置於檢測狀態(detached state),而此時調用線程就能夠發現(discover)被等待線程的資源。應當注意的是,當pthread_exit調用結束時,他的rval_ptr的值還是有效的。這就意味着若是rval_ptr指向的內存在調用線程的堆棧(Stack)上分配,那麼rval_ptr在被使用的時候它指向的內存的內容可能已經改變。舉例來講,若是一個線程在它的堆棧上給一個struct結構分配了一塊內存,並將struct結構做爲參數傳遞給了pthread_exit函數,那麼當pthread_join的調用線程 在使用rval_ptr時,這個結構可能已經被銷燬而他指向的內存可能已經用於他處。爲了不這種狀況,咱們應當使用全局變量或者在堆(Heap)上給結構分配內存。

 

一個線程能夠經過pthread_cancel方法請求取消同一進程中另外一線程的執行:

1 #include <pthread.h>
2 
3 /* Return: 0 if OK,error number on failuer */
4 int pthread_cancel(pthread_t tid);

默認狀況下,調用pthread_cancel 函數會使tid線程的行爲就像它本身使用PTHREAD_CANCELED參數調用了pthread_exit同樣。線程能夠選擇忽略或其餘的處理方式來處理cancel請求。pthread_cancel不會等待線程結束,它幾乎只是發送cancel請求。

 

線程能夠安排在它退出時須要執行的函數,這些函數通常是一些線程清理句柄(thread cleanup handlers) 。一個線程能夠創建多個清理句柄,這些句柄存儲在堆棧(stack)中,即他們會按註冊時的順序逆序執行。

1 #include <pthread.h>
2 
3 /* 註冊清理函數 */
4 void pthread_cleanup_push(void (*rtn) (void* ), void* arg);
5 
6 /* 移除棧頂的清理函數,若是excute不是0,將執行清理函數 */
7 void pthread_cleanup_pop(int excute);

pthread_cleanup_push註冊的函數在如下三種狀況下會被調用:

  1. 線程調用了 pthread_exit 函數
  2. 線程響應cancel請求
  3. 線程使用非0參數調用了pthread_cleanup_pop函數。

不管pthread_cleanup_pop函數再被調用時是否使用了非0參數,他都會將棧頂的pthread_cleanup_push註冊的清理函數移除掉。注意, return並不會執行註冊的清理函數,咱們不該該在pthread_cleanup_push和pthread_cleanup_pop之間使用return,惟一可行的辦法是在他們之間調用pthread_exit。

 

默認狀況下,一個線程的退出狀態會一直保留,除非咱們對這個線程調用pthread_join函數(調用後線程處於detached 狀態)。若是一個線程被distach了,那麼當它退出時它的底層存儲會被當即回收;可使用thread_detach函數來detach一個線程:

1 #include <pthread.h>
2 
3 /* Return: 0 if OK, error number if failure */
4 int pthread_depatch(pthread_t tid);

 

 

線程與進程函數對照表:

進程主要函數 線程主要函數 描述
fork pthread_create 建立新的實例
exit pthread_exit 退出
waitpid pthread_join 等待實例結束並獲取結束碼
atexit   pthread_cleanup_push 註冊推出前要執行的函數
getpid pthread_self 獲取實例ID
abort pthread_cancel 請求終止實例
相關文章
相關標籤/搜索