Chapter11 線程

1. void 和void* 的使用數據結構

    void 意思是無類型,void* 意思是無類型指針,能夠指向全部數據類型。函數

    (1) void 主要用於對函數參數和返回值的限定,加上void 限定則代表該函數不接受參數或者無返回值(由於在C語言中,即便函數聲明時無參數,在函數調用時傳進參數是不會報錯的,即便函數沒有指定返回類型,函數會默認返回int類型,因此加上void 限定是十分有必要的)ui

    (2) void* 則多用於函數參數(或返回值)能夠是任意類型的狀況下,由於void* 能夠接受任意類型而無需強制裝換,但將其轉換爲具體類型則須要強制裝換,這有點相似於C++中的繼承關係。spa

2. 線程建立與線程標識操作系統

    線程ID 用pthread_t 數據結構來表示,在不一樣的操做系統中表示方法不一樣,因此可移植的操做系統實現不能把它做爲整數處理,所以必須使用 pthread_equal() (函數原型:int pthread_equal(pthread_t tid1, pthread_t tid2);)函數來對像個線程ID 進行比較。調用 pthread_self 函數能夠獲取自身線程ID。線程

    在傳統Unix進程模型中,每一個進程只有一個線程。在POSIX線程的狀況下, 當程序開始運行時,也是以單進程中的單個控制線程啓動的,並能夠經過pthread_create函數建立新線程。(函數原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rth)(void*), void *restrict arg);) 其中tidp爲 ID,attr爲訂製線程屬性,start_rth 是線程開始運行的函數地址,arg是該函數的參數(若由多個參數,則使用結構體)指針

    《APUE》 程序清單 11-1 打印線程ID:rest

 1 #include "apue.h"
 2 #include <pthread.h>
 3 
 4 pthread_t ntid;
 5 
 6 void printids(const char* s) {
 7     pid_t     pid;
 8     pthread_t tid;
 9 
10     pid = getpid();
11     tid = pthread_self();
12     printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
13         (unsigned int)tid, (unsigned int)tid);
14 }
15 
16 void* thr_fn(void* arg) {
17     printids("new thread: ");
18     return ((void*)0);
19 }
20 
21 int main()
22 {
23     int err;
24     err = pthread_create(&ntid, NULL, thr_fn, NULL);
25     if (err != 0)
26         err_quit("can't create thread: %s\n", strerror(err));
27     printids("main thread: ");
28 
29     sleep(1);
30     exit(0);
31 }

    程序運行結果:code

     main thread:  pid 20961 tid 4253615936 (0xfd890740)
     new thread:  pid 20961 tid 4245337856 (0xfd0ab700)
   進程ID 相同, 線程ID 不一樣blog

3. 線程終止

    終止線程有三種方式,在不終止整個進程的狀況下終止它的控制流(注意進程中任一線程調用了exit, _Exit 或者_exit ,那麼整個進程就會終止)

  • 線程只是從啓動歷程中返回,返回值是線程的退出碼 (return)
  • 線程能夠被同一進程中的其餘線程取消。(int pthread_cancel(phread_ tid);)
  • 線程調用pthread_exit. (函數原型: void pthread_exit(void *rval_ptr);  )

    有時程序要了保證在主進程結束前線程運行結束,會在主進程中調用sleep。也可使用pthread_join (函數原型: int pthread_join(pthread_t thread, void** rval_ptr);  )

    函數,調用該函數時,調用線程會一直阻塞,直到指定的線程調用pthread_exit,從啓動例程中返回或者別取消。另外經過pthread_join函數還能夠訪問到 pthread_exit()

    參數中的rval_ptr 指針。

 

4. 線程清理處理程序(thread cleanup handler)

    線程能夠安排它退出時須要調用的函數,這些處理程序記錄在棧中,可使用pthread_cleanup_push() 函數註冊多個清理處理程序。

    在下面三種狀況下,已經註冊的清理處理程序將會被調用

  •     調用pthread_exit時 (注意return時不會調用)
  •     相應取消請求時
  •     用非零參數調用pthread_cleanup_pop 時 (與pthread_cleanup_push相對應)

     

5. 線程同步

    在變量修改時間多於一個存儲器(非原子性)訪問週期的處理器結構中,且當存儲器讀與存儲器寫這兩個週期交叉時,潛在的不一致性就會出現。若是修改操做是原子操做,那麼就不存在競爭。

*互斥量:能夠經過pthread的虎吃接口保護數據,確保同一時間只有一個線程訪問數據。

    pthread_mutex_t   互斥變量數據類型

    pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t * restrict attr) 對互斥量進行初始化,attr = NULL 則使用默認屬性;也可以使用常量     

                PTHREAD_MUTEX_INITIALIZER 進行初始化

    pthread_mutex_destroy(pthread_mutex_t* mutex); 動態分配的互斥量(如malloc),釋放內存前要調用該函數

    pthread_mutex_lock(phtread_mutex_t* mutex); 對互斥量進行加鎖

    pthread_mutex_unlock(phtread_mutex_t* mutex); 對互斥量解鎖

    pthread_mutex_trylock(pthread_mutex_t* mutex) 嘗試加鎖,未阻塞返回0, 不然失敗返回EBUSY

 

* 避免死鎖

    死鎖能夠經過當心地控制互斥量加鎖的順序來避免。

    如下代碼中,每一個foo 結構都由 一個f_lock 互斥量來保護該數據結構的引用次數,另外還有一個hashlock 來維護一個用於跟蹤foo數據結構的散列列表,這裏爲了不死鎖須要當心地維護f_lock 和hashlock 的加鎖順序。

    《APUE》程序清單 11-6 使用兩個互斥量

#include <stdlib.h>
#include <pthread.h>

#define NHASH 29
#define HASH(fp) (((unsigned long)fp)%NHASH)

struct foo* fh[NHASH];

pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;

struct foo {
    int             f_count;
    pthread_mutex_t f_lock;
    struct foo*     f_next;
    int             f_id;
};

struct foo* foo_alloc(void)
{
    struct foo* fp;
    int    idx;

    if ((fp = malloc(sizeof(struct foo))) != NULL) {
        fp->f_count = 1;
        if (pthread_mutex_init(&fp->f_lock, NULL) != NULL) {
            free(fp);
            return (NULL);
        }
        idx = HASH(fp);
        pthread_mutex_lock(&hashlock);
        fp->f_next = fh[idx];
        fh[idx] = fp;
        pthread_mutex_lock(&fp->f_lock);
        pthread_mutex_unlock(&hashlock);

        pthread_mutex_unlock(&fp->f_lock);
    }
    return (fp);
}

void foo_hold(struct foo* fp)
{
    pthread_mutex_lock(&fp->f_lock);
    fp->f_count++;
    pthread_mutex_unlock(&fp->f_lock);
}

struct foo* foo_find(int id)
{
    struct foo* fp;
    int    idx;

    idx = HASH(fp);
    pthread_mutex_lock(&hashlock);
    for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
        if (fp->f_id == id) {
            foo_hold(fp);
            break;
        }
    }
    pthread_mutex_unlock(&hashlock);
    return (fp);
}

void foo_rele(struct foo* fp)
{
    struct foo* tfp;
    int    idx;

    pthread_mutex_lock(&fp->f_lock);
    if (fp->f_count == 1) {
        pthread_mutex_unlock(&fp->f_lock);
        pthread_mutex_lock(&hashlock);
        pthread_mutex_lock(&fp->f_lock);

        /* need to recheck the condition */
        if (fp->f_count != 1) {
            fp->f_count--;
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_unlock(&hashlock);
            return;
        }

        /* remove from list */
        idx = HASH(fp);
        tfp = fh[idx];
        if (tfp == fp) {
            fh[idx] = fp->f_next;
        } else {
            while (tfp != fp)
                tfp = tfp->f_next;
            tfp->f_next = fp->f_next;
        }

        pthread_mutex_unlock(&hashlock);
        pthread_mutex_unlock(&fp->f_lock);
        pthread_mutex_destory(&fp->f_lock);
    } else {
        fp->f_count--;
        pthread_mutex_unlock(&fp->f_lock);
    }
}

 

    程序11-6 能夠只經過hashlock 來保護引用計數來使事情大大簡化,而結構互斥量f_lock 則可用來保護foo結構中的其餘任何東西。

相關文章
相關標籤/搜索