APUE之線程屬性



1. 多線程的優勢:
    [1]. 通過合理的分配任務到多個線程,每個線程在進行事件處理時可以採用同步編程模式,相比異步編程,同步編程簡單方便很多
    [2]. 多個進程間進行數據交互必須通過各種IPC機制,而同一個進程下的多個線程間共享同一片地址空間,數據可以直接交互
    [3]. 通過合理的分配任務到多個線程,可以改善整個程序的響應時間和併發性能,即便是運行在單核CPU上,只要不是CPU密集型的程序,都可以通過多線程改善性能

2. POSIX線程模型
    linux正式支持POSIX線程模型大約是從kernel-2.6和glibc-2.3開始,具體的名叫 Native POSIX Threads Library(NPTL),有關NPTL的深入分析將會另寫一份筆記
    編寫基於POSIX線程的程序時,需要在代碼中加入"#include <pthread.h>",然後在編譯代碼時加入"-lpthread"即可
    編寫基於POSIX線程的程序時,對於出錯的處理通常不要依賴errno,而是應該基於pthread系列函數返回的錯誤碼進行出錯處理

3. 線程ID(pthread_t)
    類似於進程ID的定義,線程ID用於在所在進程中唯一標識一個線程,雖然在linux中使用無符號長整型表示pthread_t,但不建議在實際操作中直接當作整數處理
    /* API  : int pthread_equal(pthread_t tid1,pthread_t tid2)
     * 描述 : 用來比較兩個線程ID,相等返回非0,不相等返回0
     */

    /* API  : pthread_t pthread_self(void)
     * 描述 : 獲取自身的線程ID
     */

4. 線程屬性(pthread_attr_t)
    線程屬性對象pthread_attr_t中包含了多個屬性,但pthread_attr_t的內部結構細節被隱藏在了NPTL中,應用程序通過NPTL提供的一組API來管理線程屬性
    線程屬性包括以下這些:
                線程的分離狀態                 
                線程的棧屬性
                線程的調度策略                 
                線程的調度參數
                線程的繼承性
                線程的作用域
                線程的棧末尾警戒緩衝區大小
    線程屬性對象在使用前必須經過初始化,相對應的,使用完畢後也必須執行銷燬
    /* API  : int pthread_attr_init(pthread_attr_t *attr)
     * 描述 : 初始化一個線程屬性對象attr
     * @attr    - 指向要被初始化的線程屬性對象
     *
     * 備註 : 初始化之後,attr中存放的就是線程屬性的默認值;
     *        不允許對已經初始化的線程屬性對象重複進行初始化
     */

    /* API  : int pthread_attr_destroy(pthread_attr_t *attr)
     * 描述 : 銷燬一個線程屬性對象attr
     * @attr    - 指向要被銷燬的線程屬性對象
     *
     * 備註 : 銷燬線程屬性對象並不會對已經創建的線程(創建時使用了該對象)有任何影響;
     *        線程屬性對象在被銷燬之後,又可以被重新初始化
     */
   
    線程的分離狀態屬性決定了線程終止時的行爲:
            非分離的線程在結束時不會自行釋放佔用的系統資源,而是要等到有線程對該線程調用了pthread_join時纔會完全釋放;
            已經分離的線程在結束時會立即釋放自身佔用的系統資源,並且其他線程也不允許再調用pthread_join來等待它的終止狀態
    /* API  : int pthread_attr_getdetachstate(const pthread_attr_t *attr,int *detachstate)
     * 描述 : 獲取線程屬性對象中的分離狀態屬性
     * @attr        - 指向線程屬性對象
     * @detachstate - 用於存放線程的分離狀態屬性:
     *                                      PTHREAD_CREATE_DETACHED - 以分離狀態啓動線程
     *                                      PTHREAD_CREATE_JOINABLE - 以非分離狀態啓動線程(默認屬性)
     */

    /* API  : int pthread_attr_setdetachstate(pthread_attr_t *attr,int *detachstate)
     * 描述 : 修改線程屬性對象中的分離狀態屬性
     * @attr        - 指向線程屬性對象
     * @detachstate - 用於設置線程的分離狀態屬性
     *
     * 備註 : 如果創建線程前就確定不需要關心該線程的終止狀態,就調用本函數提前將線程的分離狀態屬性設置爲PTHREAD_CREATE_DETACHED
     */

    /* API  : int pthread_detach(pthread_t tid)
     * 描述 : 分離指定線程
     * @tid     - 要被分離的線程ID
     *
     * 備註 : 如果對已經存在的某個線程的終止狀態不再關心,就調用本函數將其分離
     */

    線程的棧屬性又包括了棧地址、棧大小以及棧末尾的警戒緩衝區大小
    /* API  : int pthread_attr_getstack(const pthread_attr_t *attr,void **stackaddr,size_t *stacksize)
     * 描述 : 獲取線程屬性對象中的自定義的棧地址和棧大小屬性
     * @attr        - 指向線程屬性對象
     * @stackaddr   - 用於存放自定義的線程的棧地址
     * @stacksize   - 用於存放自定義的線程的棧大小
     *
     * 備註 : (glibc2.19 + kernel3.16.36環境) 本函數只能獲取通過pthread_attr_setstack設置的自定義的線程棧地址和棧大小屬性,
     *        如果沒有設置過自定義值,本函數返回空值。
     *        本函數的這個特性APUE並未闡明,網上的資料大都是相互抄襲,人云亦云。
     *        這裏,我只能說,本函數的這個特性至少是跟glibc和kernel的版本相關的
     */
   
    /* API  : int pthread_attr_setstack(pthread_attr_t *attr,void *stackaddr,size_t stacksize)
     * 描述 : 自定義線程屬性對象中的棧地址和棧大小屬性
     * @attr        - 指向線程屬性對象
     * @stackaddr   - 用於設置自定義的線程棧地址(通常要保證page邊界對齊),該地址將會作爲線程棧的最低可尋址地址
     * @stacksize   - 用於設置自定義的線程棧大小(通常要保證page大小的整數倍)
     *
     * 備註 : 自定義線程棧的步驟:
     *                          [1]. 調用malloc/mmap系列函數從堆中分配空間
     *                          [2]. 調用本函數改變新建線程的棧位置
     *                          [3]. 創建線程
     */

    /* API  : int pthread_attr_getstacksize(pthread_attr_t *attr,size_t *stacksize)
     * 描述 : 獲取線程屬性對象中的棧大小屬性
     * @attr        - 指向線程屬性對象
     * @stacksize   - 用於存放線程棧大小
     *
     * 備註 : 不同於pthread_attr_getstack,如果沒有設置過自定義值,本函數返回缺省的棧大小(通常就是8M);如果設置了自定義值,本函數返回自定義值
     */

    /* API  : int pthread_attr_setstacksize(pthread_attr_t *attr,size_t stacksize)
     * 描述 : 設置線程屬性對象中的棧大小屬性
     * @attr        - 指向線程屬性對象
     * @stacksize   - 用於設置自定義的線程棧大小(不能小於PTHREAD_STACK_MIN,並且通常要保證STACK_ALIGN的整數倍)
     *
     * 備註 : 相比pthread_attr_setstack,本函數適用場景:即希望改變默認的棧大小,又不想自己處理線程棧的分配問題
     */

    /* API  : int pthread_attr_getguardsize(pthread_attr_t *attr,size_t *guardsize)
     * 描述 : 獲取線程屬性對象中的棧末尾警戒緩衝區大小屬性
     * @attr        - 指向線程屬性對象
     * @guardsize   - 用於存放棧末尾警戒緩衝區大小(其缺省值一般是page大小)
     *
     * 備註 : 棧末尾警戒緩衝區用於避免棧溢出,具體的原理詳見另一篇棧相關的文檔
     *        APUE中寫道,"if we change the stackaddr thread attribute, the system assumes that we will be
     *        managing our own stacks and disables stack guard buffers, just as if we had set the
     *        guardsize thread attribute to 0"
     *        在本文的測試環境Debian 8.3(glibc2.19 + kernel3.16.36)中,結果並非這樣:在調用了pthread_attr_setstack之後,guardsize的值仍舊是缺省的page大小
     */

    /* API  : int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize)
     * 描述 : 設置線程屬性對象中的棧末尾警戒緩衝區大小屬性
     * @attr        - 指向線程屬性對象
     * @guardsize   - 用於設置警戒緩衝區大小
     *
     * 備註 : 雖然不被建議這麼做,但是可以將guardsize的值設爲0,意味着不再提供警戒緩衝區機制
     */
    
   
附錄:測試代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
pthread_t ntid;
pthread_attr_t attr;

void printids(const char *s)
{
    pid_t pid;
    pthread_t tid;

    pid = getpid();
    tid = pthread_self();

    printf("%s pid = %lu,tid = %lu(0x%lx)\n",s,(unsigned long)pid,(unsigned long)tid,(unsigned long)tid);
}

void *thr_fn(void *arg)
{
    printids("new thread:");

    return (void *)2;
}
int main()
{
    int err;

    if(pthread_attr_init(&attr)){
        printf("init pthread attr error\n");
        return -1;
    }

    int detachstate;
    if(pthread_attr_getdetachstate(&attr,&detachstate)){
        printf("get pthread's detach state error\n");
        return -1;
    }
    printf("pthread's detach state = %s\n",detachstate == PTHREAD_CREATE_JOINABLE?"joinable":"detach");

    size_t len = 1024 * 1000 * 4;
    void *buf = malloc(len);
    printf("malloc pthread stack addr = %p, len = %d\n",buf,len);
    if(pthread_attr_setstack(&attr,buf,len)){
        printf("set ptherad's stack attr error\n");
        return -1;
    }
    printf("set pthread stack attr ok\n");

    void *stackaddr;
    size_t stacksize;
    if(pthread_attr_getstack(&attr,&stackaddr,&stacksize)){
        printf("get ptherad's stack attr error\n");
        return -1;
    }
    printf("pthread's stack addr = %p,size = %d\n",stackaddr,stacksize);

    if(pthread_attr_getstacksize(&attr,&stacksize)){
        printf("get ptherad's stack size error\n");
        return -1;
    }
    printf("pthread's stack size = %d\n",stacksize);

    size_t guardsize;
    if(pthread_attr_getguardsize(&attr,&guardsize)){
        printf("get ptherad's stack size error\n");
        return -1;
    }
    printf("pthread's guard size = %d\n",guardsize);

    err = pthread_create(&ntid,NULL,thr_fn,NULL);
    if(err){
        printf("create ptherad error = %d\n",err);
        return -1;
    }
    printids("main thread:");
    sleep(1);

    if(pthread_attr_destroy(&attr)){
        printf("destory ptherad attr error \n");
        return -1;
    }

    void *state;
    if(pthread_join(ntid,&state)){
        printf("join ptherad error \n");
        return -1;
    }
    printf("join pthread ok,state = %d\n",(unsigned int)state);

    exit(0);
}

發佈了61 篇原創文章 · 獲贊 15 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章