1. 程式人生 > >7.9-UC-第九課:執行緒管理

7.9-UC-第九課:執行緒管理

================ 第九課  執行緒管理 ================
一、基本概念 ------------
1. 執行緒就是程式的執行路線,即程序內部的控制序列,    或者說是程序的子任務。
2. 執行緒,輕量級,不擁有自己獨立的記憶體資源,    共享程序的程式碼區、資料區、堆區(注意沒有棧區)、    環境變數和命令列引數、檔案描述符、訊號處理函式、    當前目錄、使用者ID和組ID等資源。
3. 執行緒擁有自己獨立的棧,因此也有自己獨立的區域性變數。
4. 一個程序可以同時擁有多個執行緒,    即同時被系統排程的多條執行路線,    但至少要有一個主執行緒。
二、基本特點 ------------
1. 執行緒是程序的一個實體,    可作為系統獨立排程和分派的基本單位。
2. 執行緒有不同的狀態,系統提供了多種執行緒控制原語,    如建立執行緒、銷燬執行緒等等。
3. 執行緒不擁有自己的資源,只擁有從屬於程序的全部資源,    所有的資源分配都是面向程序的。
4. 一個程序中可以有多個執行緒併發地執行。    它們可以執行相同的程式碼,也可以執行不同的程式碼。
5. 同一個程序的多個執行緒都在同一個地址空間內活動,    因此相對於程序,執行緒的系統開銷小,任務切換快。
6. 執行緒間的資料交換不需要依賴於類似IPC的特殊通訊機制,    簡單而高效。
7. 每個執行緒擁有自己獨立的執行緒ID、暫存器資訊、函式棧、    錯誤碼和訊號掩碼。
8. 執行緒之間存在優先順序的差異。
三、POSIX執行緒(pthread) ----------------------
1. 早期廠商各自提供私有的執行緒庫版本,    介面和實現的差異非常大,不易於移植。
2. IEEE POSIX 1003.1c (1995)標準,    定義了統一的執行緒程式設計介面,    遵循該標準的執行緒實現被統稱為POSIX執行緒,即pthread。
3. pthread包含一個頭檔案pthread.h,    和一個介面庫libpthread.so。
#include <pthread.h>
...
gcc ... -lpthread
4. 功能
1) 執行緒管理:建立/銷燬執行緒、分離/聯合執行緒、    設定/查詢執行緒屬性。
2) 執行緒同步
A. 互斥量:建立/銷燬互斥量、加鎖/解鎖互斥量、    設定/查詢互斥量屬性。
B. 條件變數:建立/銷燬條件變數、等待/觸發條件變數、    設定/查詢條件變數屬性。
四、執行緒函式 ------------
1. 建立執行緒 ~~~~~~~~~~~
int pthread_create (pthread_t* restrict thread,     const pthread_attr_t* restrict attr,     void* (*start_routine) (void*),     void* restrict arg);
thread        - 執行緒ID,輸出引數。                 pthread_t即unsigned long int。
attr          - 執行緒屬性,NULL表示預設屬性。                 pthread_attr_t可能是整型也可能是結構,                 因實現而異。
start_routine - 執行緒過程函式指標,                 引數和返回值的型別都是void*。                 啟動執行緒本質上就是呼叫一個函式,                 只不過是在一個獨立的執行緒中呼叫的,                 函式返回即執行緒結束。
arg           - 傳遞給執行緒過程函式的引數。                 執行緒過程函式的呼叫者是系統核心,                 而非使用者程式碼,                 因此需要在建立執行緒時指定引數。
成功返回0,失敗返回錯誤碼。
注意:
1) restrict: C99引入的編譯優化指示符,    提高重複解引用同一個指標的效率。
2) 在pthread.h標頭檔案中宣告的函式,    通常以直接返回錯誤碼的方式表示失敗,    而非以錯誤碼設定errno並返回-1。
3) main函式即主執行緒,main函式返回即主執行緒結束,    主執行緒結束即程序結束,    程序一但結束其所有的執行緒即結束。
4) 應設法保證線上程過程函式執行期間,    其引數所指向的目標持久有效。
建立執行緒。範例:create.c
執行緒併發。範例:concur.c
執行緒引數。範例:arg.c
2. 等待執行緒 ~~~~~~~~~~~
int pthread_join (pthread_t thread, void** retval);
等待thread引數所標識的執行緒結束, 成功返回0,失敗返回錯誤碼。
範例:ret.c
注意從執行緒過程函式中返回值的方法:
1) 執行緒過程函式將所需返回的內容放在一塊記憶體中,    返回該記憶體的地址,保證這塊記憶體在函式返回,    即執行緒結束,以後依然有效;
2) 若retval引數非NULL,    則pthread_join函式將執行緒過程函式所返回的指標,    拷貝到該引數所指向的記憶體中;
3) 若執行緒過程函式所返回的指標指向動態分配的記憶體,    則還需保證在用過該記憶體之後釋放之。
3. 獲取執行緒自身的ID ~~~~~~~~~~~~~~~~~~~
pthread_t pthread_self (void);
成功返回呼叫執行緒的ID,不會失敗。
4. 比較兩個執行緒的ID ~~~~~~~~~~~~~~~~~~~
int pthread_equal (pthread_t t1, pthread_t t2);
若引數t1和t2所標識的執行緒ID相等,則返回非零,否則返回0。
某些實現的pthread_t不是unsigned long int型別, 可能是結構體型別,無法通過“==”判斷其相等性。
範例:equal.c
5. 終止執行緒 ~~~~~~~~~~~
1) 從執行緒過程函式中return。
2) 呼叫pthread_exit函式。
void pthread_exit (void* retval);
retval - 和執行緒過程函式的返回值語義相同。
注意:在任何執行緒中呼叫exit函式都將終止整個程序。
範例:exit.c
6. 執行緒執行軌跡 ~~~~~~~~~~~~~~~
1) 同步方式(非分離狀態):    建立執行緒之後呼叫pthread_join函式等待其終止,    並釋放執行緒資源。
2) 非同步方式(分離狀態):    無需建立者等待,執行緒終止後自行釋放資源。
int pthread_detach (pthread_t thread);
使thread引數所標識的執行緒進入分離(DETACHED)狀態。 處於分離狀態的執行緒終止後自動釋放執行緒資源, 且不能被pthread_join函式等待。
成功返回0,失敗返回錯誤碼。
範例:detach.c
7. 取消執行緒 ~~~~~~~~~~~
1) 向指定執行緒傳送取消請求
int pthread_cancel (pthread_t thread);
成功返回0,失敗返回錯誤碼。
注意:該函式只是向執行緒發出取消請求, 並不等待執行緒終止。
預設情況下,執行緒在收到取消請求以後,並不會立即終止, 而是仍繼續執行,直到其達到某個取消點。在取消點處, 執行緒檢查其自身是否已被取消了,並做出相應動作。
當執行緒呼叫一些特定函式時,取消點會出現。
2) 設定呼叫執行緒的可取消狀態
int pthread_setcancelstate (int state,     int* oldstate);
成功返回0,並通過oldstate引數輸出原可取消狀態 (若非NULL),失敗返回錯誤碼。
state取值:
PTHREAD_CANCEL_ENABLE  - 接受取消請求(預設)。
PTHREAD_CANCEL_DISABLE - 忽略取消請求。
3) 設定呼叫執行緒的可取消型別
int pthread_setcanceltype (int type, int* oldtype);
成功返回0,並通過oldtype引數輸出原可取消型別 (若非NULL),失敗返回錯誤碼。
type取值:
PTHREAD_CANCEL_DEFERRED     - 延遲取消(預設)。
被取消執行緒在接收到取消請求之後並不立即響應, 而是一直等到執行了特定的函式(取消點)之後再響應該請求。
PTHREAD_CANCEL_ASYNCHRONOUS - 非同步取消。
被取消執行緒可以在任意時間取消, 不是非得遇到取消點才能被取消。 但是作業系統並不能保證這一點。
範例:cancel.c
8. 執行緒屬性 ~~~~~~~~~~~
建立執行緒函式 int pthread_create (pthread_t* restrict thread,     const pthread_attr_t* restrict attr,     void* (*start_routine) (void*),     void* restrict arg); 的第二個引數即為執行緒屬性,傳空指標表示使用預設屬性。
typedef struct {     // 分離狀態     //     // PTHREAD_CREATE_DETACHED     // - 分離執行緒。     //     // PTHREAD_CREATE_JOINABLE(預設)     // - 可匯合執行緒。     //     int detachstate;
    // 競爭範圍     //     // PTHREAD_SCOPE_SYSTEM     // - 在系統範圍內競爭資源。     //     // PTHREAD_SCOPE_PROCESS(Linux不支援)     // - 在程序範圍內競爭資源。     //     int scope;
    // 繼承特性     //     // PTHREAD_INHERIT_SCHED(預設)     // - 排程屬性自建立者執行緒繼承。     //     // PTHREAD_EXPLICIT_SCHED     // - 排程屬性由後面兩個成員確定。     //     int inheritsched;
    // 排程策略     //     // SCHED_FIFO     // - 先進先出策略。     //     // 沒有時間片。     //     // 一個FIFO執行緒會持續執行,     // 直到阻塞或有高優先順序執行緒就緒。     //     // 當FIFO執行緒阻塞時,系統將其移出就緒佇列,     // 待其恢復時再加到同優先順序就緒佇列的末尾。     //     // 當FIFO執行緒被高優先順序執行緒搶佔時,     // 它在就緒佇列中的位置不變。     // 因此一旦高優先順序執行緒終止或阻塞,     // 被搶佔的FIFO執行緒將會立即繼續執行。     //     // SCHED_RR     // - 輪轉策略。     //     // 給每個RR執行緒分配一個時間片,     // 一但RR執行緒的時間片耗盡,     // 系統即將移到就緒佇列的末尾。     //     // SCHED_OTHER(預設)     // - 普通策略。     //     // 靜態優先順序為0。任何就緒的FIFO執行緒或RR執行緒,     // 都會搶佔此類執行緒。     //     int schedpolicy;
    // 排程引數     //     // struct sched_param {     //     int sched_priority; /* 靜態優先順序 */     // };     //     struct sched_param schedparam;
    // 棧尾警戒區大小(位元組)     //     // 預設一頁(4096位元組)。     //     size_t guardsize;
    // 棧地址     //     void* stackaddr;
    // 棧大小(位元組)     //     size_t stacksize; }   pthread_attr_t;
不要手工讀寫該結構體, 而應呼叫pthread_attr_set/get函式設定/獲取具體屬性項。
1) 設定執行緒屬性
第一步,初始化執行緒屬性結構體
int pthread_attr_init (pthread_attr_t* attr);
第二步,設定具體執行緒屬性項
int pthread_attr_setdetachstate (     pthread_attr_t* attr,     int detachstate);
int pthread_attr_setscope (     pthread_attr_t* attr,     int scope);
int pthread_attr_setinheritsched (     pthread_attr_t* attr,     int inheritsched);
int pthread_attr_setschedpolicy (     pthread_attr_t* attr,     int policy);
int pthread_attr_setschedparam (     pthread_attr_t* attr,     const struct sched_param* param);
int pthread_attr_setguardsize (     pthread_attr_t* attr,     size_t guardsize);
int pthread_attr_setstackaddr (     pthread_attr_t* attr,     void* stackaddr);
int pthread_attr_setstacksize (     pthread_attr_t* attr,     size_t stacksize);
int pthread_attr_setstack (     pthread_attr_t* attr,     void* stackaddr, size_t stacksize);
第三步,以設定好的執行緒屬性結構體為引數建立執行緒
int pthread_create (pthread_t* restrict thread,     const pthread_attr_t* testrict attr,     void* (*start_routine) (void*),     void* restrict arg);
第四步,銷燬執行緒屬性結構體
int pthread_attr_destroy (pthread_attr_t* attr);
2) 獲取執行緒屬性
第一步,獲取執行緒屬性結構體
int pthread_getattr_np (pthread_t thread,     pthread_attr_t* attr);
第二步,獲取具體執行緒屬性項
int pthread_attr_getdetachstate (     pthread_attr_t* attr,     int* detachstate);
int pthread_attr_getscope (     pthread_attr_t* attr,     int* scope);
int pthread_attr_getinheritsched (     pthread_attr_t* attr,     int* inheritsched);
int pthread_attr_getschedpolicy (     pthread_attr_t* attr,     int* policy);
int pthread_attr_getschedparam (     pthread_attr_t* attr,     struct sched_param* param);
int pthread_attr_getguardsize (     pthread_attr_t* attr,     size_t* guardsize);
int pthread_attr_getstackaddr (     pthread_attr_t* attr,     void** stackaddr);
int pthread_attr_getstacksize (     pthread_attr_t* attr,     size_t* stacksize);
int pthread_attr_getstack (     pthread_attr_t* attr,     void** stackaddr, size_t* stacksize);
以上所有函式成功返回0,失敗返回錯誤碼。
範例:attr.c

來自為知筆記(Wiz)