目錄
前言
7. 執行緒
7.1 概念
-
程式:程式是資源管理的最小單位
-
執行緒:執行緒是程式執行的最小單位
-
因為程式開銷大,才衍生出執行緒
- 程式切換上下文時, 需要重新對映虛擬地址空間、進出OS核心、暫存器切換,還會干擾處理器的快取機制
-
一個程式至少需要一個執行緒作為它的指令執行體,程式管理著資源(比如cpu、記憶體、檔案等等), 而將執行緒分配到某個cpu上執行
-
新的執行執行緒將擁有自己的棧,但與它的建立者共享全域性變數、檔案描述符、訊號處理函式和當前目錄狀態
-
特點:
- 一個程式至少有一個程式,一個程式至少有一個執行緒
- 執行緒使用程式的資源,程式崩潰,執行緒也隨之崩潰
- 執行緒切換上下文快,程式切換上下文慢
-
使用 pthread_create 建立執行緒
-
使用 int pthread_attr_destroy(pthread_attr_t *attr); 函式來銷燬一個執行緒屬性物件
-
使用 pthread_attr_init() 函式可以初始化執行緒物件的屬性
-
使用 pthread_join() 等待執行緒結束
-
使用 pthread_detach() 來設定執行緒為分離狀態
7.2 建立執行緒
7.2.1 pthread_create()
- 使用 pthread_create 建立執行緒
- 通過命令 man 瞭解更多
- 所需要的標頭檔案:
#include <pthread.h>
- 函式原型:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
- thread:指向執行緒識別符號的指標
- attr:設定執行緒屬性
- start_routine:函式指標,執行緒入口
- arg:傳給執行緒入口函式的引數
7.3 設定執行緒屬性
- 執行緒屬性結構體 pthread_attr_t:
typedef struct{ int etachstate; //執行緒的分離狀態 int schedpolicy; //執行緒排程策略 structsched_param schedparam; //執行緒的排程引數 int inheritsched; //執行緒的繼承性 int scope; //執行緒的作用域 size_t guardsize; //執行緒棧末尾的警戒緩衝區大小 int stackaddr_set; //執行緒的棧設定 void* stackaddr; //執行緒棧的位置 size_t stacksize; //執行緒棧的大小 }pthread_attr_t;
- 注意:
- 因為 pthread 並非 Linux 系統的預設庫,而是 POSIX執行緒庫。在Linux中將其作為一個庫來使用, 因此編譯時需要加上-lpthread(或-pthread)以顯式指定連結該庫
- 函式在執行錯誤時,並不把錯誤資訊寫到變數 error 中,而是作為返回值返回
- 執行緒屬性不能直接設定,只能通過函式操作
- 執行緒屬性包括:作用域(scope)、棧大小(stacksize)、棧地址(stackaddress)、優先順序(priority)、 分離的狀態(detachedstate)、排程策略和引數(scheduling policy and parameters)
- 執行緒的預設屬性:非繫結、非分離、1M的堆疊大小、與父程式同樣級別的優先順序
- API:
API | 說明 |
---|---|
pthread_attr_init() | 初始化一個執行緒物件的屬性 |
pthread_attr_destroy() | 銷燬一個執行緒屬性物件 |
pthread_attr_getaffinity_np() | 獲取執行緒間的CPU親緣性 |
pthread_attr_setaffinity_np() | 設定執行緒的CPU親緣性 |
pthread_attr_getdetachstate() | 獲取執行緒分離狀態屬性 |
pthread_attr_setdetachstate() | 修改執行緒分離狀態屬性 |
pthread_attr_getguardsize() | 獲取執行緒的棧保護區大小 |
pthread_attr_setguardsize() | 設定執行緒的棧保護區大小 |
pthread_attr_getscope() | 獲取執行緒的作用域 |
pthread_attr_setscope() | 設定執行緒的作用域 |
pthread_attr_getstack() | 獲取執行緒的堆疊資訊(棧地址和棧大小) |
pthread_attr_setstack() | 設定執行緒堆疊區 |
pthread_attr_getstacksize() | 獲取執行緒堆疊大小 |
pthread_attr_setstacksize() | 設定執行緒堆疊大小 |
pthread_attr_getschedpolicy() | 獲取執行緒的排程策略 |
pthread_attr_setschedpolicy() | 設定執行緒的排程策略 |
pthread_attr_setschedparam() | 獲取執行緒的排程優先順序 |
pthread_attr_getschedparam() | 設定執行緒的排程優先順序 |
pthread_attr_getinheritsched() | 獲取執行緒是否繼承排程屬性 |
pthread_attr_getinheritsched() | 設定執行緒是否繼承排程屬性 |
7.3.1 pthread_attr_init()
- 使用 pthread_attr_init 初始化執行緒物件屬性
- 通過命令 man 瞭解更多
- 所需要的標頭檔案:
#include <pthread.h>
- 函式原型:
int pthread_attr_init(pthread_attr_t *attr);
- attr:指向一個執行緒屬性的指標
- 返回:
- 成功:返回 0
- 失敗:返回 非 0
7.3.2 銷燬一個執行緒屬性物件
- 使用
int pthread_attr_destroy(pthread_attr_t *attr);
函式來銷燬一個執行緒屬性物件- attr:指向一個執行緒屬性的指標
- 返回:
- 成功:返回 0
- 失敗:返回 錯誤碼
7.3.3 執行緒的分離狀態
-
在任何一個時間點上,執行緒是可結合的(joinable),或者是分離的(detached)
- 可結合的執行緒:能夠被其他執行緒收回其資源和殺死;在被其他執行緒回收之前,它的儲存器資源(如棧)是不釋放的
- 分離的執行緒:不能被其他執行緒回收或殺死的,它的儲存器資源在它終止時由系統自動釋放
-
執行緒的分離狀態決定一個執行緒以什麼樣的方式來終止自己
-
預設狀態下是 非分離狀態
-
函式:
int pthread_join(pthread_t tid, void **rval_ptr);
- 等待某個執行緒的終止,獲得該執行緒的終止狀態,並收回所佔的資源
-
- tid:執行緒識別符號
- rval_ptr設定為NULL,則忽略返回狀態
int pthread_detach(pthread_t tid);
- 將執行緒設定為分離狀態
- 標頭檔案:
#include <pthread.h>
- tid:執行緒識別符號
- 返回:
- 成功:返回 0
- 失敗:返回一個錯誤值:
- EINVAL:tid 對應的執行緒不是一個 非分離的執行緒
- ESRCH:找不到對應的執行緒
-
在非分離狀態下:
- 原有執行緒等待建立的執行緒結束,只有當 pthread_join() 函式放回時,建立的執行緒才算真正終止
-
在分離狀態下:
- 該執行緒沒有被其他的執行緒所等待,自己執行結束了,執行緒也就終止了,馬上釋放系統資源
-
如果在建立執行緒時就知道不需要了解執行緒的終止狀態,則可以 pthread_attr_t 結構中的 detachstate 執行緒屬性,讓執行緒以分離狀態啟動
- 使用
pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
函式來設定執行緒分離狀態- attr:指向一個執行緒屬性的指標
- detachstate:
- PTHREAD_CREATE_DETACHED:分離執行緒
- PTHREAD _CREATE_JOINABLE:非分離執行緒
- 使用
-
獲取某個執行緒的分離狀態
- 使用
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
函式獲取執行緒的分離狀態 - attr:指向一個執行緒屬性的指標
- detachstate:儲存狀態的值
- 返回:
- 成功:返回 0
- 失敗:返回 錯誤碼
- 使用
-
注意:
- 如果設定一個執行緒為分離執行緒,而這個執行緒執行又非常快,它很可能在 pthread_create 函式返回之前就終止了,它終止以後就可能將執行緒號和系統資源移交給其他的執行緒使用,這樣呼叫 pthread_create 的執行緒就得到了錯誤的執行緒號。要避免這種情況可以採取一定的同步措施,最簡單的方法之一是可以在被建立的執行緒裡呼叫 pthread_cond_timewait 函式,讓這個執行緒等待一會兒,留出足夠的時間讓函式 pthread_create 返回。設定一段等待時間,是在多執行緒程式設計裡常用的方法。但是注意不要使用諸如 wait() 之類的函式,它們是使整個程式睡眠,並不能解決執行緒同步的問題。
7.3.4 執行緒的排程策略
-
POSIX 標準指定了三種排程策略:
- 普通執行緒(預設)SCHED_OTHER。採用時分排程策略,不需要實時機制。另外兩種用於超級使用者許可權時執行。
- 實時執行緒SCHDE_FIFO。採用實時排程-先進先出方式策略。一旦佔用cpu則一直執行,直到有更高優先順序任務到達或自己放棄。
- 輪詢排程SCHED_RR。採用實時排程-時間片輪轉方式排程策略。當任務的時間片用完,系統將重新分配時間片,並置於就緒佇列尾。放在佇列尾保證了所有具有相同優先順序的RR任務的排程公平。所以說SCHED_RR=SCHED_OTHER+SCHDE_FIFO。
-
與排程相關的API
- 引數說明:
- attr:指向一個執行緒屬性的指標。
- inheritsched:執行緒是否繼承排程屬性,可選值分別為
- PTHREAD_INHERIT_SCHED:排程屬性將繼承於建立的執行緒,attr中設定的排程屬性將被忽略。
- PTHREAD_EXPLICIT_SCHED:排程屬性將被設定為attr中指定的屬性值。
- policy:可選值為執行緒的三種排程策略
- SCHED_OTHER
- SCHED_FIFO
- SCHED_RR
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched); int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
- 引數說明:
7.3.5 執行緒優先順序
- SCHED_OTHER排程時
- 靜態優先順序必須置為 0
- 處於靜態優先順序為 0 的執行緒按照動態優先順序被排程
- 動態優先順序值起始於 nice 值,且當前執行緒處於就緒態並被排程器無視時,其動態值++,以保證競爭CPU的公平性
- 執行緒優先順序的設定(靜態優先順序)(SCHED_FIFO和SCHED_RR)
- 通過以下函式來獲得執行緒可以設定的最高和最低優先順序(不支援SCHED_OTHER)
int sched_get_priority_max(int policy); int sched_get_priority_min(int policy);
- 通過以下兩個函式來設定和獲取優先順序
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
- 執行緒優先順序特點
- 新執行緒的優先順序預設為 0
- 新執行緒不繼承父執行緒排程優先順序(PTHREAD_EXPLICIT_SCHED)
- 當執行緒的排程策略為SCHED_OTHER時,不允許修改執行緒優先順序,僅當排程策略為實時(即SCHED_FIFO或SCHED_RR)時才有效, 並可以在執行時通過pthread_setschedparam()函式來改變,預設為0。
7.3.6 執行緒棧
- 執行緒棧:
- 用於存放函式形參、區域性變數、執行緒切換現場暫存器等資料。
- 使用的是程式的地址空間,預設執行緒棧大小是 1M
- 設定和獲取執行緒大小可以使用一下函式
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
7.4 退出執行緒
- 執行緒退出使用
void pthread_exit(void *retval);
函式 - 注意:不能使用 exit() 函式退出,因為該函式是直接退出程式的,會把程式內所有函式都退出。
參考
* 野火