【linux】系統編程-5-線程


前言

7. 線程

7.1 概念

  • 進程:進程是資源管理的最小單位html

  • 線程:線程是程序執行的最小單位編程

  • 由於進程開銷大,才衍生出線程緩存

    • 進程切換上下文時, 須要從新映射虛擬地址空間、進出OS內核、寄存器切換,還會干擾處理器的緩存機制
  • 一個進程至少須要一個線程做爲它的指令執行體,進程管理着資源(好比cpu、內存、文件等等), 而將線程分配到某個cpu上執行多線程

  • 新的執行線程將擁有本身的棧,但與它的建立者共享全局變量、文件描述符、信號處理函數和當前目錄狀態函數

  • 特色:線程

    • 一個程序至少有一個進程,一個進程至少有一個線程
    • 線程使用進程的資源,進程崩潰,線程也隨之崩潰
    • 線程切換上下文快,進程切換上下文慢
  • 使用 pthread_create 建立線程指針

  • 使用 int pthread_attr_destroy(pthread_attr_t *attr); 函數來銷燬一個線程屬性對象code

  • 使用 pthread_attr_init() 函數能夠初始化線程對象的屬性htm

  • 使用 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 標準指定了三種調度策略:

    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
    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() 函數退出,由於該函數是直接退出進程的,會把進程內全部函數都退出。

參考

* 野火
相關文章
相關標籤/搜索