第11章 執行緒

2021-07-25 12:37:22 字數 4784 閱讀 9782

1. 執行緒的作用

(1)  對於程式設計而言:當我們的乙個程序在某一時刻,需要做的事件不止一件的時候,一般有兩種方法。一種是採用非同步程式設計的模式,一種是採用多執行緒同步模式。但是多執行緒同步模式遠遠比非同步模式要方便的多。但是對於單核系統,往往非同步程式設計模式效率更高。

(2)  對於互動程式,一般都是要多執行緒,因為在處理資料的時候,介面都是響應的。

每個執行緒都包含有表示執行環境所必須的資訊,其中包括程序中標識執行緒id、一組暫存器值、棧、排程優先順序和策略、訊號遮蔽字、error變數以及執行緒私有資料。乙個程序的所有資訊都是對該程序的所有執行緒都是共享的。

2. 執行緒標識

#include //比較執行緒id是否相等:0---不等;非0---相等

int pthread_equal(pthread_t t1, pthread_t t2);

//獲得執行緒自身的id

pthread_t pthread_self();

3. 執行緒建立

/*

建立執行緒:0---成功;否則,返回錯誤編號

成功時,新建立的執行緒id會被設定成 th 指向的記憶體單元。

attr 引數用於定製各種不同的執行緒屬性,null---預設屬性。

新建立的執行緒從 func 函式的位址開始執行,

該函式只有乙個無型別的指標引數 arg

*/int pthread_create(pthread_t *th,

const pthread_attr_t *attr,

void *(*func)(void *),

void *arg);

//注:執行緒建立時並不能保證哪個引數會先執行:是建立執行緒還是呼叫執行緒。

4. 執行緒終止

執行緒的三種退出方式:

(1)可以簡單地從啟動例程中返回,返回值是執行緒的退出碼

(2)執行緒可以被同一程序中的其他執行緒取消

(3)執行緒呼叫pthread_exit

//退出當前執行緒:res 跟 pthread_create() 函式中的 void* arg 類似。

//程序中的其他執行緒可以通過使用 pthread_join() 函式獲得這個指標

void pthread_exit(void *res);

/*阻塞等待執行緒結束函式:呼叫執行緒將一直阻塞,直到指定的執行緒呼叫

pthread_exit、從啟動例程中返回或者被取消. 0---成功,否則錯誤編號。

如果執行緒簡單的從它的啟動例程中返回,res就包含返回碼;

如果執行緒被取消,res指定的記憶體單元就被設定為 pthread_canceled

*/int pthread_join(pthread_t t, void **res);

//執行緒可以通過呼叫該函式來請求取消同一程序中的其他執行緒.

//注:pthread_cancel並不等待執行緒終止,它僅僅退出請求

int pthread_cancel(pthread_t t);

//執行緒可以安排它退出時需要呼叫的函式,乙個執行緒可以安排多個這樣的函式,

//這樣的函式記錄在棧中,執行時與註冊順序相反。這種函式叫清理函式

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

void pthread_cleanup_pop(int execute);

/*注:如果 execute 引數設定為0,清理函式將不被呼叫。

當呼叫 pthread_exit()時,響應取消請求時,用非零引數呼叫

pthread_cleanup_pop時,pthread_cleanup_pop都將刪除「上次」

pthread_cleanup_push呼叫建立的清除處理函式,所以這兩個函式必須

在與執行緒相同的作用域中以配對形式使用

而如果執行緒是簡單的從啟動例程中返回的而終止的話,它的清除處理函式

就不會被呼叫。而且由於平台不同,這種返回在freebsd和mac os x上回出現

段錯誤,解決方法使用pthread_exit().

*///下面這個程序函式,如果 condition==true 從中間返回,那麼

//func_clean 就不會被執行

void *func(void *arg)

pthread_cleanup_pop(0);

return((void *)1);

}//如果執行緒已經分離,呼叫pthread_join會產生未定義的行為。

//我們呼叫pthread_detach來分離執行緒. 0---成功,否則返回錯誤編號

int pthread_detach(pthread_t t);

5. 執行緒同步

(1) 互斥量

/*

互斥變數是用 pthread_mutex_t 資料型別表示的,在使用互斥變數之前

必須首先對其進行初始化。對於靜態分配的互斥量(靜態或全域性)可以設定為常量

pthread_mutex_initializer(只適用於靜態分配的互斥量),也可以

通過 pthread_mutex_init() 初始。以下:0---成功,否則返回錯誤編號

*/int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a);

int pthread_mutex_destroy(pthread_mutex_t *m);

//加鎖、解鎖:0---成功,否則返回錯誤編號

int pthread_mutex_lock(pthread_mutex_t *m);

int pthread_mutex_trylock(pthread_mutex_t *m);

int pthread_mutex_unlock(pthread_mutex_t *m);

(2) 避免死鎖

如果同乙個執行緒試圖對同乙個互斥量加鎖兩次,就會出現死鎖。另外,如果執行緒1獲得了互斥量a,又要請求互斥量b,而執行緒2在獲得了互斥量b又要請求a,那麼就會出現死鎖。第二種情況下,我們可以使用 pthread_mutex_trylock 介面避免死鎖。如果已經占有某些鎖而且pthread_mutex_trylock介面返回成功,那麼就可以前進。但如果不能獲取鎖,可以先釋放已經占有的鎖,做好清理工作,然後過一段時間再重新試。或者,我們可以總是讓a、b互斥量以相同的順序加鎖來避免死鎖。

同時,注意鎖的粒度也不要太粗或太細,而影響程式的併發性或增加系統的開銷。

(3) 函式pthread_mutex_timedlock

/*

當執行緒試圖獲取乙個已加鎖的互斥量時,該函式允許繫結執行緒阻塞時間。

但是在達到超時時間時,pthread_mutex_timedlock不會對互斥量

進行加鎖,而是返回錯誤碼 etimedout;但是如果在指定時間內互斥量

被解鎖,那麼該函式執行成功後會將該互斥量鎖住

返回值:0---成功,否則返回錯誤編號

*/#include #include int pthread_mutex_timedlock(pthread_mutex_t *m, const timespec *ts);

//注:這個時間指的是絕對時間,就是如果在這個截止時間之前 m 被解鎖,

//那麼這個函式立即返回,不會死等到指定的結束時間

(4) 讀寫鎖

讀寫鎖允許更高的並行性。一次只有乙個執行緒可以占有寫模式的讀寫鎖,但是多個執行緒可以同時占有讀模式的讀寫鎖。當讀寫鎖是寫加鎖的狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞。當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對他對它進行加鎖的執行緒都可以得到訪問,但是任何以寫模式對此鎖進行加鎖的執行緒都會阻塞,直到所有的執行緒釋放它們的讀鎖為止。但是當讀寫鎖處於讀模式鎖住的狀態,而這時有乙個執行緒試圖以寫模式獲取該鎖時,讀寫鎖通常會阻塞隨後的讀模式鎖請求。

//讀寫鎖:0---成功,否則返回錯誤編號

//對於靜態分配的互斥量可以設定為常量

//pthread_rwlock_initializer(只適用於靜態分配的互斥量)

int pthread_rwlock_init(pthread_rwlock_t *rwlock_,

const pthread_rwlockattr_t *attr);

int pthread_rwlock_destroy(pthread_rwlock_t *l);

//加鎖、解鎖:0---成功,否則返回錯誤編號

int pthread_rwlock_rdlock(pthread_rwlock_t *l);

int pthread_rwlock_wrlock(pthread_rwlock_t *l);

int pthread_rwlock_unlock(pthread_rwlock_t *l);

int pthread_rwlock_tryrdlock(pthread_rwlock_t *l);

int pthread_rwlock_trywrlock(pthread_rwlock_t *l);

(5) 帶有超時的讀寫鎖

//帶有超時的讀寫鎖:0---成功,否則返回錯誤編號

int pthread_rwlock_timedrdlock(pthread_rwlock_t *l, const timespec *ts);

int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const timespec *ts);

(6) 條件變數

第11章 執行緒

執行緒標識 就像每個程序有乙個程序id一樣,每個執行緒也有乙個執行緒id。程序id在整個系統中是唯一的,但執行緒id不同,執行緒id只有在它所屬的程序上下文中才有意義。程序id 用pid t資料型別表示 執行緒id用pthread t資料型別來表示 includeint pthread equal ...

第11章執行緒總結

1 執行緒標識 pthread t型別 int pthread equal pthread t tid1,pthread t tid2 pthread t pthread self 2 建立執行緒 int pthread create pthread t tid,pthread attr t att...

第11章 函式

1 把相關的語句組合在一起,並且賦予相應的名稱,用這種方法來給程式分塊,這種形式的組合就是函式,函式也叫例程或者過程。程式總是從 main 函式開始啟動。函式由函式名 引數 返回值型別以及一組包含操作語句的語句塊組成。函式可以支援過載,程式就是由函式組成。2 形參是函式定義時在形參表中定義的,並且由...