使用pthread庫進行多執行緒程式設計1 - UNIX環境高階程式設計第11章讀書筆記

ATField發表於2007-03-11

11 Threads

1 Introduction

不用介紹了吧

2 Thread Concepts

1.     Thread由下面部分組成:

a.     Thread ID

b.     Stack

c.     Policy

d.     Signal mask

e.     Errno

f.      Thread-Specific Data

3 Thread Identification

1.     pthread_t用於表示Thread ID,具體內容根據實現的不同而不同,有可能是一個Structure,因此不能將其看作為整數

2.     pthread_equal函式用於比較兩個pthread_t是否相等

#include <pthread.h>

 

int pthread_equal(pthread_t tid1, pthread_t tid2)

3.     pthread_self函式用於獲得本執行緒的thread id

#include <pthread.h>

 

pthread _t pthread_self(void);

 

4 Thread Creation

1.     建立執行緒可以呼叫pthread_create函式:

#include <pthread.h>

 

int pthread_create(

       pthread_t *restrict tidp,

       const pthread_attr_t *restrict attr,

       void *(*start_rtn)(void *), void *restrict arg);

a.     pthread_t *restrict tidp:返回最後建立出來的ThreadThread ID

b.     const pthread_attr_t *restrict attr:指定執行緒的Attributes,後面會講道,現在可以用NULL

c.     void *(*start_rtn)(void *):指定執行緒函式指標,該函式返回一個void *,引數也為void*

d.     void *restrict arg:傳入給執行緒函式的引數

e.     返回錯誤值。

2.     pthread函式在出錯的時候不會設定errno,而是直接返回錯誤值

3.     Linux系統下面,在老的核心中,由於Thread也被看作是一種特殊,可共享地址空間和資源的Process,因此在同一個Process中建立的不同Thread具有不同的Process ID(呼叫getpid獲得)。而在新的2.6核心之中,Linux採用了NPTL(Native POSIX Thread Library)執行緒模型(可以參考http://en.wikipedia.org/wiki/Native_POSIX_Thread_Libraryhttp://www-128.ibm.com/developerworks/linux/library/l-threading.html?ca=dgr-lnxw07LinuxThreadsAndNPTL,在該執行緒模型下同一程式下不同執行緒呼叫getpid返回同一個PID。

4.     不能對建立的新執行緒和當前建立者執行緒的執行順序作出任何假設

5 Thread Termination

1.     exit, _Exit, _exit用於中止當前程式,而非執行緒

2.     中止執行緒可以有三種方式:

a.     線上程函式中return

b.     被同一程式中的另外的執行緒Cancel

c.     執行緒呼叫pthread_exit函式

3.     pthread_exitpthread_join函式的用法:

a.     執行緒A呼叫pthread_join(B, &rval_ptr),被Block,進入Detached狀態(如果已經進入Detached狀態,則pthread_join函式返回EINVAL)。如果對B的結束程式碼不感興趣,rval_ptr可以傳NULL

b.     執行緒B呼叫pthread_exit(rval_ptr),退出執行緒B,結束程式碼為rval_ptr。注意rval_ptr指向的記憶體的生命週期,不應該指向BStack中的資料。

c.     執行緒A恢復執行,pthread_join函式呼叫結束,執行緒B的結束程式碼被儲存到rval_ptr引數中去。如果執行緒BCancel,那麼rval_ptr的值就是PTHREAD_CANCELLED

兩個函式原型如下:

#include <pthread.h>

 

void pthread_exit(void *rval_ptr);

 

int pthread_join(pthread_t thread, void **rval_ptr);

4.     一個Thread可以要求另外一個ThreadCancel,通過呼叫pthread_cancel函式:

#include <pthread.h>

 

void pthread_cancel(pthread_t tid)

該函式會使指定執行緒如同呼叫了pthread_exit(PTHREAD_CANCELLED)。不過,指定執行緒可以選擇忽略或者進行自己的處理,在後面會講到。此外,該函式不會導致Block,只是傳送Cancel這個請求。

5.     執行緒可以安排在它退出的時候,某些函式自動被呼叫,類似atexit()函式。需要呼叫如下函式:

#include <pthread.h>

 

void pthread_cleanup_push(void (*rtn)(void *), void *arg);

void pthread_cleanup_pop(int execute);

這兩個函式維護一個函式指標的Stack,可以把函式指標和函式引數值push/pop。執行的順序則是從棧頂到棧底,也就是和push的順序相反。

在下面情況下pthread_cleanup_push所指定的thread cleanup handlers會被呼叫:

a.     呼叫pthread_exit

b.     相應cancel請求

c.     以非0引數呼叫pthread_cleanup_pop()。(如果以0呼叫pthread_cleanup_pop(),那麼handler不會被呼叫

有一個比較怪異的要求是,由於這兩個函式可能由巨集的方式來實現,因此這兩個函式的呼叫必須得是在同一個Scope之中,並且配對,因為在pthread_cleanup_push的實現中可能有一個{,而pthread_cleanup_pop可能有一個}。因此,一般情況下,這兩個函式是用於處理意外情況用的,舉例如下:

void *thread_func(void *arg)

{

    pthread_cleanup_push(cleanup, “handler”)

 

    // do something

 

    Pthread_cleanup_pop(0);

    return((void *)0)

}

 

6.     程式函式和執行緒函式的相關性:

Process Primitive

Thread Primitive

Description

fork

pthread_create

建立新的控制流

exit

pthread_exit

退出已有的控制流

waitpid

pthread_join

等待控制流並獲得結束程式碼

atexit

pthread_cleanup_push

註冊在控制流退出時候被呼叫的函式

getpid

pthread_self

獲得控制流的id

abort

pthread_cancel

請求非正常退出

7.     預設情況下,一個執行緒A的結束狀態被儲存下來直到pthread_join為該執行緒被呼叫過,也就是說即使執行緒A已經結束,只要沒有執行緒B呼叫pthread_join(A)A的退出狀態則一直被儲存。而當執行緒處於Detached狀態之時,黨執行緒退出的時候,其資源可以立刻被回收,那麼這個退出狀態也丟失了。在這個狀態下,無法為該執行緒呼叫pthread_join函式。我們可以通過呼叫pthread_detach函式來使指定執行緒進入Detach狀態:

#include <pthread.h>

 

int pthread_detach(pthread_t tid);

通過修改呼叫pthread_create函式的attr引數,我們可以指定一個執行緒在建立之後立刻就進入Detached狀態

6 Thread Synchronization

1.     互斥量:Mutex

a.     用於互斥訪問

b.     型別:pthread_mutex_t,必須被初始化為PTHREAD_MUTEX_INITIALIZER(用於靜態分配的mutex,等價於pthread_mutex_init(…, NULL))或者呼叫pthread_mutex_initMutex也應該用pthread_mutex_destroy來銷燬。這兩個函式原型如下:(attr的具體含義下一章討論)

#include <pthread.h>

 

int pthread_mutex_init(

       pthread_mutex_t *restrict mutex,

       const pthread_mutexattr_t *restrict attr)

 

int pthread_mutex_destroy(pthread_mutex_t *mutex);

c.     pthread_mutex_lock用於Lock Mutex,如果Mutex已經被Lock,該函式呼叫會Block直到MutexUnlock,然後該函式會Lock Mutex並返回。pthread_mutex_trylock類似,只是當MutexLock的時候不會Block,而是返回一個錯誤值EBUSYpthread_mutex_unlock則是unlock一個mutex。這三個函式原型如下:

#include <pthread.h>

 

int pthread_mutex_lock(pthread_mutex_t *mutex);

 

int pthread_mutex_trylock(pthread_mutex_t *mutex);

 

int pthread_mutex_unlock(pthread_mutex_t *mutex);

 

2.     讀寫鎖:Reader-Writer Locks

a.     多個執行緒可以同時獲得讀鎖(Reader-Writer lock in read mode),但是隻有一個執行緒能夠獲得寫鎖(Reader-writer lock in write mode)

b.     讀寫鎖有三種狀態

                                          i.    一個或者多個執行緒獲得讀鎖,其他執行緒無法獲得寫鎖

                                         ii.    一個執行緒獲得寫鎖,其他執行緒無法獲得讀鎖

                                        iii.    沒有執行緒獲得此讀寫鎖

c.     型別為pthread_rwlock_t

d.     建立和關閉方法如下:

#include <pthread.h>

 

int pthread_rwlock_init(

       pthread_rwlock_t *restrict rwlock,

       const pthread_rwlockattr_t *restrict attr)

 

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

e.     獲得讀寫鎖的方法如下:

#include <pthread.h>

 

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

 

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

 

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

 

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

 

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

 

pthread_rwlock_rdlock:獲得讀鎖

pthread_rwlock_wrlock:獲得寫鎖

pthread_rwlock_unlock:釋放鎖,不管是讀鎖還是寫鎖都是呼叫此函式

注意具體實現可能對同時獲得讀鎖的執行緒個數有限制,所以在呼叫pthread_rwlock_rdlock的時候需要檢查錯誤值,而另外兩個pthread_rwlock_wrlockpthread_rwlock_unlock則一般不用檢查,如果我們程式碼寫的正確的話。

3.     Conditional Variable:條件

a.     條件必須被Mutex保護起來

b.     型別為:pthread_cond_t,必須被初始化為PTHREAD_COND_INITIALIZER(用於靜態分配的條件,等價於pthread_cond_init(…, NULL))或者呼叫pthread_cond_init

#include <pthread.h>

 

int pthread_cond_init(

       pthread_cond_t *restrict cond,

       const pthread_condxattr_t *restrict attr)

 

int pthread_cond_destroy(pthread_cond_t *cond);

 

c.     pthread_cond_wait函式用於等待條件發生(=true)。pthread_cond_timedwait類似,只是當等待超時的時候返回一個錯誤值ETIMEDOUT。超時的時間用timespec結構指定。此外,兩個函式都需要傳入一個Mutex用於保護條件

#include <pthread.h>

 

int pthread_cond_wait(

       pthread_cond_t *restrict cond,

       pthread_mutex_t *restrict mutex);

 

int pthread_cond_timedwait(

       pthread_cond_t *restrict cond,

       pthread_mutex_t *restrict mutex,

       const struct timespec *restrict timeout);

 

d.     timespec結構定義如下:

struct timespec {

       time_t tv_sec;       /* seconds */

       long   tv_nsec;      /* nanoseconds */

};

注意timespec的時間是絕對時間而非相對時間,因此需要先呼叫gettimeofday函式獲得當前時間,再轉換成timespec結構,加上偏移量。

e.     有兩個函式用於通知執行緒條件被滿足(=true):

#include <pthread.h>

 

int pthread_cond_signal(pthread_cond_t *cond);

 

int pthread_cond_broadcast(pthread_cond_t *cond);

兩者的區別是前者會喚醒單個執行緒,而後者會喚醒多個執行緒。

 

 

相關文章