1. 程式人生 > >【linux】系統程式設計-5-執行緒

【linux】系統程式設計-5-執行緒

[toc] --- ## 前言 * [原文連結](https://www.cnblogs.com/lizhuming/p/14231490.html) ## 7. 執行緒 ### 7.1 概念 * **程序:程序是資源管理的最小單位** * **執行緒:執行緒是程式執行的最小單位** * 因為程序開銷大,才衍生出執行緒 * 程序切換上下文時, 需要重新對映虛擬地址空間、進出OS核心、暫存器切換,還會干擾處理器的快取機制 * 一個程序至少需要一個執行緒作為它的指令執行體,程序管理著資源(比如cpu、記憶體、檔案等等), 而將執行緒分配到某個cpu上執行 * ![](https://img2020.cnblogs.com/blog/2085252/202101/2085252-20210104183328212-1597902459.png) * 新的執行執行緒將擁有自己的棧,但與它的建立者共享全域性變數、檔案描述符、訊號處理函式和當前目錄狀態 * 特點: * 一個程式至少有一個程序,一個程序至少有一個執行緒 * 執行緒使用程序的資源,程序崩潰,執行緒也隨之崩潰 * 執行緒切換上下文快,程序切換上下文慢 * 使用 **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** 瞭解更多 * 所需要的標頭檔案: ```c #include ``` * 函式原型:**`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**: ```c 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** 瞭解更多 * 所需要的標頭檔案: ```c #include ``` * 函式原型:**`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 `** * **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 標準指定了三種排程策略: 1. 普通執行緒(預設)**SCHED_OTHER**。採用時分排程策略,不需要實時機制。另外兩種用於超級使用者許可權時執行。 2. 實時執行緒**SCHDE_FIFO**。採用實時排程-先進先出方式策略。一旦佔用cpu則一直執行,直到有更高優先順序任務到達或自己放棄。 3. 輪詢排程**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 ```c 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) ```c int sched_get_priority_max(int policy); int sched_get_priority_min(int policy); ``` * 通過以下兩個函式來設定和獲取優先順序 ```c 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** * 設定和獲取執行緒大小可以使用一下函式 ```c 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()** 函式退出,因為該函式是直接退出程序的,會把程序內所有函式都退出。 ## 參考