1. 概要
執行緒的同步,發生在多個執行緒共享相同記憶體的時候,這時,要保證每個執行緒在每個時刻看到的共享資料是一致的。如果每個執行緒使用的變數都是其他執行緒不會使用的(read & write),或者變數是唯讀的,就不存在一致性問題。但是,如果兩個或兩個以上的執行緒可以read / write乙個變數時,就需要對執行緒進行同步,以確保它們在訪問該變數時,不會得到無效的值,同時也可以唯一地修改該變數並使它生效。
以上就是我們所說的執行緒同步。
執行緒同步有三種常用的機制:互斥量(muex) ,讀寫鎖(rwlock)和條件變數(cond) 。
互斥量:有兩種狀態:lock 和 unlock,它確保同一時間只有乙個執行緒訪問資料;
讀寫鎖:有三種狀態:讀加鎖,寫加鎖,不加鎖,只有乙個執行緒可以占有寫模式的讀寫鎖,但是可以有多個執行緒同時占有讀模式的讀寫鎖;
條件變數:給多執行緒提供了乙個會合的場所,與互斥量一起使用時,允許執行緒以無競爭的方式等待特定條件的發生。
2. 互斥量 (mutex)
互斥量從本質上說就是一把鎖,提供對共享資源的保護訪問。
(1) 初始化:
在linux下,執行緒的互斥量資料型別是pthread_mutex_t。在使用前,要對它進行初始化:
對於靜態分配的互斥量,可以把它設定為pthread_mutex_initializer,或者呼叫pthread_mutex_init。
對於動態分配的互斥量,在申請記憶體(malloc)之後,通過pthread_mutex_init進行初始化,並且在釋放記憶體(free)前需要呼叫pthread_mutex_destroy。
原型:int pthread_mutex_init(pthread_mutex_t* restrict mutex, const pthread_mutexattr_t* restrict attr);
int pthread_mutex_init(pthread_mutex_t* mutex);
標頭檔案:
返回值:成功則返回0,出錯則返回錯誤編號
說明:如果使用預設的屬性初始化互斥量,只需把attr設為null,其他值在以後講解。
(2) 互斥操作
對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,呼叫執行緒會阻塞,直到互斥量被解鎖。在完成了對貢獻資源的訪問後,要對互斥量進行解鎖。
首先說一下加鎖函式:
標頭檔案:
原型:int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_tyrlock(pthread_mutex_t* mutex);
返回值:成功則返回0,出錯則返回錯誤編號。
說明:具體說一下trylock函式,這個函式是非阻塞呼叫模式,也就是說,如果互斥量沒被鎖住,trylock函式將把互斥量加鎖,並獲得對共享資源的訪問許可權;如果互斥量被鎖住了,trylock函式將不會阻塞等待而直接返回ebusy,表示共享資源處於忙狀態。
再說一下解鎖函式:
標頭檔案:
原型:int pthread_mutex_unlock(pthread_mutex_t* mutex);
返回值:成功則返回0,出錯則返回錯誤編號。
(3) 死鎖:
死鎖主要發生在有多個依賴鎖存在時,會在乙個執行緒試圖與另乙個執行緒相反順序鎖住互斥量時發生,如何避免死鎖是使用互斥量應格外注意的東西。
總體來講,有幾個不成文的基本原則:
對共享資源操作前一定要獲得鎖;
完成操作以後一定要釋放鎖;
盡量短時間地占用鎖;
如果有多鎖,如獲得順序是abc連環扣,釋放順序也應該是abc;
執行緒錯誤返回時應釋放它所獲得的鎖。
3. 讀寫鎖
讀寫鎖是因為有3種狀態,所以有更高的並行性。
(1) 特性:
一次只有乙個執行緒可以占有寫模式的讀寫鎖,但是可以有多個執行緒同時占有讀模式的讀寫鎖,正是因為這個特性,
當讀寫鎖是寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的執行緒都會被阻塞。
當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的執行緒都可以得到訪問權,但是如果執行緒希望以寫模式對此鎖進行加鎖,它必須阻塞直到所有執行緒釋放鎖。
通常,當讀寫鎖處於讀模式鎖住狀態時,如果有另外執行緒試圖以寫模式加鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求,這樣可以避免讀模式鎖長期占用而等待的寫模式鎖請求長期阻塞。
(2) 適用性:
讀寫鎖適合於對資料結構的讀次數比寫次數多得多的情況。 因為,讀模式鎖定時可以共享,以寫模式鎖住時意味著獨佔,所以,讀寫鎖又叫共享-獨佔鎖。
(3) 初始化和銷毀:
#include
int pthread_rwlock_init(pthread_rwlock_t* restrict rwlock, const pthread_rwlockattr_t* restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
成功則返回0,出錯則返回錯誤編號。
同互斥量以上,在釋放讀寫鎖占用的記憶體之前,需要先通過pthread_rwlock_destroy對讀寫鎖進行清理工作,釋放有init分配的資源。
(4) 讀和寫:
#include
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
成功則返回0,出錯則返回錯誤編號。
這三個函式分別實現獲取讀鎖,獲取寫鎖和釋放鎖的操作,獲取鎖的兩個函式是阻塞操作,同樣,非阻塞的函式為:
#include
int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock);
成功則返回0,出錯則返回錯誤編號。
非阻塞的獲取鎖操作,如果可以獲取則返回0,否則返回錯誤的ebusy。
4. 條件變數
條件變數分為兩部分:條件和變數。條件本身是由互斥量保護的。執行緒在改變條件狀態前先要鎖住互斥量。
(1) 初始化:
條件變數採用的資料型別是pthread_cond_t,在使用之前必須要進行初始化,這包括兩種方式:
靜態:可以把常量pthread_cond_initializer給靜態分配的條件變數。
動態:pthread_cond_init函式,釋放動態條件變數的記憶體空間之前,要用pthread_cond_destroy對其進行清理。
#include
int pthread_cond_init(pthread_cond_t* restrict cond, pthread_condattr_t* restrict attr);
int pthread_cond_destroy(pthread_cond_t* cond);
成功則返回0,出錯則返回錯誤編號。
當pthread_cond_init的attr引數為null,會建立乙個預設屬性的條件變數;非預設情況以後討論。
(2) 等待條件:
#include
int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex);
int pthread_cond_timewait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex, const struct timespec* restrict timeout);
成功則返回0,出錯則返回錯誤編號。
這兩個函式分別是阻塞等待和超時等待。
等待條件函式等待條件變數為真,傳遞給pthread_cond_wait的互斥量對條件進行保護,呼叫者把鎖住的互斥量傳遞給函式,函式把呼叫執行緒放到等待條件的執行緒列表上,然後對互斥量解鎖,這兩個操作是原子的。這樣便關閉了條件檢查和執行緒進入休眠狀態等待條件改變這兩個操作之間的時間通道,這樣執行緒就不會錯過條件的任何變化。
當pthread_cond_wait返回時,互斥量再次被鎖住。
(3) 通知條件:
#include
int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cont_t* cond);
成功則返回0,出錯則返回錯誤編號。
這兩個函式用於通知執行緒條件已經滿足,呼叫這個兩個函式,也稱向執行緒或條件傳送訊號。必須注意,一定要在改變條件狀態以後再給執行緒傳送訊號。
linux 執行緒 執行緒同步
因為執行緒獨自擁有的只有棧,其他的區域執行緒共同擁有。並且對共享區域的操作並不都是原子的。對共享區域的操作順序又是不確定的。就像建立兩個檔案描述符同時指向 同一檔案,並且連續向檔案中寫入那麼寫的東西可能是亂七八糟的。這時就需要執行緒對共享區的同步。而另一種情況是,多個執行緒的指令執行順序需要同步。這...
Linux執行緒同步
1.概要 執行緒的同步,發生在多個執行緒共享相同記憶體的時候,這時,要保證每個執行緒在每個時刻看到的共享資料是一致的。如果每個執行緒使用的變數都是其他執行緒不會使用的 read write 或者變數是唯讀的,就不存在一致性問題。但是,如果兩個或兩個以上的執行緒可以read write乙個變數時,就需...
linux執行緒同步
首先介紹幾個linux執行緒同步需要的函式 pthread mutex lock pthread mutex unlock pthread cond wait pthread cond signal 下面使用乙個例子介紹上面幾個函式的用法。在主線程接受使用者輸入資訊,在子執行緒中輸入使用者輸入的資訊...