pthread多線程編程整理(一)

 

轉載 pthread多線程編程整理(一)收藏

<script type="text/javascript"></script>
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是否相等

#i nclude <pthread.h>
 
int pthread_equal(pthread_t tid1, pthread_t tid2)
3.     pthread_self函數用於獲得本線程的thread id

#i nclude <pthread.h>
 
pthread _t pthread_self(void);
 
4 Thread Creation
1.     創建線程可以調用pthread_create函數:

#i nclude <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:返回最後創建出來的Thread的Thread 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_exit和pthread_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指向的內存的生命週期,不應該指向B的Stack中的數據。
c.     線程A恢復運行,pthread_join函數調用結束,線程B的結束代碼被保存到rval_ptr參數中去。如果線程B被Cancel,那麼rval_ptr的值就是PTHREAD_CANCELLED。
兩個函數原型如下:

#i nclude <pthread.h>
 
void pthread_exit(void *rval_ptr);
 
int pthread_join(pthread_t thread, void **rval_ptr);
4.     一個Thread可以要求另外一個Thread被Cancel,通過調用pthread_cancel函數:

#i nclude <pthread.h>
 
void pthread_cancel(pthread_t tid)
該函數會使指定線程如同調用了pthread_exit(PTHREAD_CANCELLED)。不過,指定線程可以選擇忽略或者進行自己的處理,在後面會講到。此外,該函數不會導致Block,只是發送Cancel這個請求。
5.     線程可以安排在它退出的時候,某些函數自動被調用,類似atexit()函數。需要調用如下函數:

#i nclude <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狀態:

#i nclude <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_init。Mutex也應該用pthread_mutex_destroy來銷燬。這兩個函數原型如下:(attr的具體含義下一章討論)

#i nclude <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直到Mutex被Unlock,然後該函數會Lock Mutex並返回。pthread_mutex_trylock類似,只是當Mutex被Lock的時候不會Block,而是返回一個錯誤值EBUSY。pthread_mutex_unlock則是unlock一個mutex。這三個函數原型如下:

#i nclude <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.     創建和關閉方法如下:

#i nclude <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.     獲得讀寫鎖的方法如下:

#i nclude <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_wrlock和pthread_rwlock_unlock則一般不用檢查,如果我們代碼寫的正確的話。
3.     Conditional Variable:條件
a.     條件必須被Mutex保護起來
b.     類型爲:pthread_cond_t,必須被初始化爲PTHREAD_COND_INITIALIZER(用於靜態分配的條件,等價於pthread_cond_init(…, NULL))或者調用pthread_cond_init

#i nclude <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用於保護條件

#i nclude <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):

#i nclude <pthread.h>
 
int pthread_cond_signal(pthread_cond_t *cond);
 
int pthread_cond_broadcast(pthread_cond_t *cond);
兩者的區別是前者會喚醒單個線程,而後者會喚醒多個線程。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章