Pthread

**POSIX 執行緒**（英語：POSIX Threads，常被縮寫為 pthreads）是 POSIX 的執行緒標準，定義了建立和操縱執行緒的一套 API。

實現 POSIX 執行緒標準的庫常被稱作 **pthreads**，一般用於 Unix-like POSIX 系統，如 Linux、 Solaris。但是 Microsoft Windows 上的實現也存在，例如直接使用 Windows API 實現的第三方庫 pthreads-w32；而利用 Windows 的 SFU/SUA 子系統，則可以使用微軟提供的一部分原生 POSIX API。

## API 具體內容

Pthreads 定義了一套 C 語言的類型、函式與常數，它以 pthread.h（頁面存檔備份，存於網際網路檔案館）標頭檔和一個執行緒庫實現。

Pthreads API 中大致共有 100 個函式呼叫，全都以 "pthread\_" 開頭，並可以分為四類：

* 執行緒管理，例如建立執行緒，等待（join）執行緒，查詢執行緒狀態等。
* 互斥鎖（Mutex）：建立、摧毀、鎖定、解鎖、設定屬性等操作
* 條件變數（Condition Variable）：建立、摧毀、等待、通知、設定與查詢屬性等操作
* 使用了互斥鎖的執行緒間的同步管理

POSIX 的 Semaphore API 可以和 Pthreads 協同工作，但這並不是 Pthreads 的標準。因而這部分 API 是以 "sem\_" 打頭，而非 "pthread\_"。

### 資料類型

* pthread\_t：執行緒控制代碼。出於移植目的，不能把它作為整數處理，應使用函式 pthread\_equal () 對兩個執行緒 ID 進行比較。取得自身所線上程 id 使用函式 pthread\_self ()。
* pthread\_attr\_t：執行緒屬性。主要包括 scope 屬性、detach 屬性、堆疊位址、堆疊大小、優先級。主要屬性的意義如下：
  * \_\_detachstate，表示新執行緒是否與行程中其他執行緒脫離同步。如果設定為 PTHREAD\_CREATE\_DETACHED，則新執行緒不能用 pthread\_join () 來同步，且在登出時自行釋放所占用的資源。預設為 PTHREAD\_CREATE\_JOINABLE 狀態。可以線上程建立並執行以後用 pthread\_detach () 來設定。一旦設定為 PTHREAD\_CREATE\_DETACHED 狀態，不論是建立時設定還是執行時設定，則不能再恢復到 PTHREAD\_CREATE\_JOINABLE 狀態。
  * \_\_schedpolicy，表示新執行緒的排程策略，包括 SCHED\_OTHER（正常、非即時）、SCHED\_RR（即時、輪轉法）和 SCHED\_FIFO（即時、先入先出）三種，預設為 SCHED\_OTHER，後兩種排程策略僅對超級使用者有效。執行時可以用過 pthread\_setschedparam () 來改變。
  * \_\_schedparam，一個 struct sched\_param 結構，目前僅有一個 sched\_priority 整型變數表示執行緒的執行優先級。這個參數僅當排程策略為即時（即 SCHED\_RR 或 SCHED\_FIFO）時才有效，並可以在執行時通過 pthread\_setschedparam () 函式來改變，預設為 0。系統支援的最大和最小的優先級值可以用函式 sched\_get\_priority\_max 和 sched\_get\_priority\_min 得到。
  * \_\_inheritsched，有兩種值可供選擇：PTHREAD\_EXPLICIT\_SCHED 和 PTHREAD\_INHERIT\_SCHED，前者表示新執行緒使用顯式指定排程策略和排程參數（即 attr 中的值），而後者表示繼承呼叫者執行緒的值。預設為 PTHREAD\_EXPLICIT\_SCHED。
  * \_\_scope，表示執行緒間競爭 CPU 的範圍，也就是說執行緒優先級的有效範圍。POSIX 的標準中定義了兩個值：PTHREAD\_SCOPE\_SYSTEM 和 PTHREAD\_SCOPE\_PROCESS，前者表示與系統中所有執行緒一起競爭 CPU 時間，後者表示僅與同行程中的執行緒競爭 CPU。目前 LinuxThreads 僅實現了 PTHREAD\_SCOPE\_SYSTEM 一值。
* pthread\_barrier\_t：同步屏障資料類型
* pthread\_mutex\_t：mutex 資料類型
* pthread\_cond\_t：條件變數資料類型

### 函式

執行緒操縱函式（簡介起見，省略參數）:

* pthread\_create()：建立一個執行緒
* pthread\_exit()：終止當前執行緒
* pthread\_cancel()：請求中斷另外一個執行緒的執行。被請求中斷的執行緒會繼續執行，直至到達某個**取消點**(Cancellation Point)。取消點是執行緒檢查是否被取消並按照請求進行動作的一個位置。POSIX 的取消類型（Cancellation Type）有兩種，一種是延遲取消 (PTHREAD\_CANCEL\_DEFERRED)，這是系統預設的取消類型，即線上程到達取消點之前，不會出現真正的取消；另外一種是非同步取消 (PHREAD\_CANCEL\_ASYNCHRONOUS)，使用非同步取消時，執行緒可以在任意時間取消。系統呼叫的取消點實際上是函式中取消類型被修改為非同步取消至修改回延遲取消的時間段。幾乎可以使執行緒掛起的庫函式都會回應 CANCEL 訊號，終止執行緒，包括 sleep、delay 等延時函式。
* pthread\_join()：阻塞當前的執行緒，直到另外一個執行緒執行結束
* pthread\_kill()：向指定 ID 的執行緒傳送一個訊號，如果執行緒不處理該訊號，則按照訊號預設的行為作用於整個行程。訊號值 0 為保留訊號，作用是根據函式的返回值判斷執行緒是不是還活著。
* pthread\_cleanup\_push()：執行緒可以安排異常登出時需要呼叫的函式，這樣的函式稱為執行緒清理程式，執行緒可以建立多個清理程式。執行緒清理程式的入口位址使用棧儲存，實行先進後處理原則。由 pthread\_cancel 或 pthread\_exit 引起的執行緒結束，會次序執行由 pthread\_cleanup\_push 壓入的函式。執行緒函式執行 return 語句返回不會引起執行緒清理程式被執行。
* pthread\_cleanup\_pop()：以非 0 參數呼叫時，引起當前被彈出的執行緒清理程式執行。
* pthread\_setcancelstate()：允許或禁止取消另外一個執行緒的執行。
* pthread\_setcanceltype()：設定執行緒的取消類型為延遲取消或非同步取消。

執行緒屬性函式：

* pthread\_attr\_init()：初始化執行緒屬性變數。執行後，pthread\_attr\_t 結構所包含的內容是作業系統支援的執行緒的所有屬性的預設值。
* pthread\_attr\_setdetachstate()：設定執行緒屬性變數的 detachstate 屬性（決定執行緒在終止時是否可以被 joinable）
* pthread\_attr\_getdetachstate()：取得脫離狀態的屬性
* pthread\_attr\_setscope()：設定執行緒屬性變數的 \_\_scope 屬性
* pthread\_attr\_setschedparam()：設定執行緒屬性變數的 schedparam 屬性，即呼叫的優先級。
* pthread\_attr\_getschedparam()：取得執行緒屬性變數的 schedparam 屬性，即呼叫的優先級。
* pthread\_attr\_destroy()：刪除執行緒的屬性，用無效值覆蓋

mutex 函式：

* pthread\_mutex\_init() 初始化互斥鎖
* pthread\_mutex\_destroy() 刪除互斥鎖
* pthread\_mutex\_lock()：占有互斥鎖（阻塞操作）
* pthread\_mutex\_trylock()：試圖占有互斥鎖（不阻塞操作）。即，當互斥鎖空閒時，將占有該鎖；否則，立即返回。
* pthread\_mutex\_unlock(): 釋放互斥鎖
* pthread\_mutexattr\_(): 互斥鎖屬性相關的函式

條件變數函式：

* pthread\_cond\_init()：初始化條件變數
* pthread\_cond\_destroy()：銷毀條件變數
* pthread\_cond\_signal(): 傳送一個訊號給正在當前條件變數的執行緒佇列中處於阻塞等待狀態的執行緒，使其脫離阻塞狀態，喚醒後繼續執行。如果沒有執行緒處在阻塞等待狀態，pthread\_cond\_signal 也會成功返回。一般只給一個阻塞狀態的執行緒發訊號。假如有多個執行緒正在阻塞等待當前條件變數，則根據各等待執行緒優先級的高低確定哪個執行緒接收到訊號開始繼續執行。如果各執行緒優先級相同，則根據等待時間的長短來確定哪個執行緒獲得訊號。但 pthread\_cond\_signal 在多處理器上可能同時喚醒多個執行緒，當只能讓一個被喚醒的執行緒處理某個任務時，其它被喚醒的執行緒就需要繼續 wait。POSIX 規範要求 pthread\_cond\_signal 至少喚醒一個 pthread\_cond\_wait 上的執行緒，有些實現為了簡便，在單處理器上也會喚醒多個執行緒。所以最好對 pthread\_cond\_wait () 使用 while 迴圈對條件變數是否滿足做條件判斷。
* pthread\_cond\_wait(): 等待條件變數的特殊條件發生；pthread\_cond\_wait () 必須與一個 pthread\_mutex 配套使用。該函式呼叫實際上依次做了 3 件事：對當前 pthread\_mutex 解鎖、把當前執行緒掛起到當前條件變數的執行緒佇列、被其它執行緒的訊號喚醒後對當前 pthread\_mutex 申請加鎖。如果執行緒收到一個訊號被喚醒，將被配套的互斥鎖重新鎖住，pthread\_cond\_wait () 函式將不返回直到執行緒獲得配套的互斥鎖。需要注意的是，一個條件變數不應該與多個互斥鎖配套使用。
* pthread\_cond\_broadcast(): 某些應用，如執行緒池，pthread\_cond\_broadcast 喚醒全部執行緒，但我們通常只需要一部分執行緒去做執行任務，所以其它的執行緒需要繼續 wait.
* pthread\_condattr\_(): 條件變數屬性相關的函式

執行緒私有儲存（Thread-local storage）:

* pthread\_key\_create(): 分配用於標識行程中執行緒特定資料的 pthread\_key\_t 類型的鍵
* pthread\_key\_delete(): 銷毀現有執行緒特定資料鍵
* pthread\_setspecific(): 為指定執行緒的特定資料鍵設定繫結的值
* pthread\_getspecific(): 取得呼叫執行緒的鍵繫結值，並將該繫結儲存在 value 指向的位置中

同步屏障函式

* pthread\_barrier\_init(): 同步屏障初始化
* pthread\_barrier\_wait():
* pthread\_barrier\_destory():

其它多執行緒同步函式：

* pthread\_rwlock\_\*(): 讀寫鎖

工具函式：

* pthread\_equal(): 對兩個執行緒的執行緒標識號進行比較
* pthread\_detach(): 分離執行緒
* pthread\_self(): 查詢執行緒自身執行緒標識號
* pthread\_once()： 某些需要僅執行一次的函式。其中第一個參數為 pthread\_once\_t 類型，是內部實現的互斥鎖，保證在程式全域僅執行一次。

號誌函式，包含在 semaphore.h 中：

* sem\_open：建立或者打開已有的命名號誌。可分為二值號誌與計數號誌。命名號誌可以在行程間共享使用。
* sem\_close：關閉一個訊號燈，但沒有將它從系統中刪除。命名訊號燈是隨核心持續的，即使當前沒有行程打開著某個訊號燈，它的值仍然保持。
* sem\_unlink：從系統中刪除訊號燈。
* sem\_getvalue：返回所指定訊號燈的當前值。如果該訊號燈當前已上鎖，那麼返回值或為 0，或為某個負數，其絕對值就是等待該訊號燈解鎖的執行緒數。
* sem\_wait：申請共享資源，所指定訊號燈的值如果大於 0，那就將它減 1 並立即返回，就可以使用申請來的共享資源了。如果該值等於 0，呼叫執行緒就被進入睡眠狀態，直到該值變為大於 0，這時再將它減 1，函式隨後返回。sem\_wait 操作必須是原子操作。
* sem\_trywait：申請共享資源，當所指定訊號燈的值已經是 0 時，後者並不將呼叫執行緒投入睡眠。相反，它返回一個 EAGAIN 錯誤。
* sem\_post：釋放共享資源。與 sem\_wait 恰相反。
* sem\_init：初始化非命名（記憶體）號誌
* sem\_destroy：摧毀非命名號誌

共享記憶體函式，包含在 sys/mman.h 中，連結時使用 rt 庫：

* mmap：把一個檔案或一個 POSIX 共享記憶體區物件對映到呼叫行程的位址空間。使用該函式的目的： 1. 使用普通檔案以提供記憶體對映 I/O 2. 使用特殊檔案以提供匿名記憶體對映。 3. 使用 shm\_open 以提供無親緣關係行程間的 Posix 共享記憶體區。
* munmap： 刪除一個對映關係
* msync：檔案與記憶體同步函式
* shm\_open：建立或打開共享記憶體區
* shm\_unlink：刪除一個共享記憶體區物件的名字，刪除一個名字僅僅防止後續的 open,msq\_open 或 sem\_open 呼叫取得成功。
* ftruncate: 調整檔案或共享記憶體區大小
* fstat 來取得有關該物件的資訊

## 例子

C 中使用 Pthreads 的範例：

這段程式建立了一個新執行緒，列印含有「b」的行，主執行緒列印含有「a」的行。當兩個執行緒相互切換執行時輸出結果為 'a' 和 'b' 交替出現。

## 參考

* David R. Butenhof：_Programming with POSIX Threads_, Addison-Wesley, ISBN 0-201-63392-2
* Bradford Nichols，Dick Buttlar，Jacqueline Proulx Farell：_Pthreads Programming_, O'Reilly & Associates, ISBN 1-56592-115-1
* Charles J. Northrup：_Programming with UNIX Threads_, John Wiley & Sons, ISBN 0-471-13751-0
* Kay A. Robbins and Steven Robbins，_UNIX Systems Programming_, Prentice-Hall, ISBN 0-13-042411-0

## 參見

* Native POSIX Thread Library (NPTL)
* Spurious wakeup
* Thread-local storage
* GNU 可移植執行緒庫
* FSU Pthreads

## 外部連結

* Pthread Win-32（頁面存檔備份，存於網際網路檔案館），Basic Programming
* Pthreads Tutorial（頁面存檔備份，存於網際網路檔案館）
* C/C++ Tutorial: using Pthreads（頁面存檔備份，存於網際網路檔案館）
* Article "POSIX threads explained（頁面存檔備份，存於網際網路檔案館）" by Daniel Robbins (Gentoo Linux 建立者)
* Interview "Ten Questions with David Butenhof about Parallel Programming and POSIX Threads（頁面存檔備份，存於網際網路檔案館）" by Michael Suess
* Open Source POSIX Threads for Win32（頁面存檔備份，存於網際網路檔案館）
* The Open Group Base Specifications Issue 6, IEEE Std 1003.1（頁面存檔備份，存於網際網路檔案館）
* GNU Portable threads（頁面存檔備份，存於網際網路檔案館）
* Pthreads Presentation at 2007 OSCON (O'Reilly Open Source Convention) by Adrien Lamothe. An overview of Pthreads with current trends.（頁面存檔備份，存於網際網路檔案館）