LINUX多執行緒互斥量和讀寫鎖區別

2021-05-27 13:14:28 字數 4668 閱讀 1194

執行緒的同步, 發生在多個執行緒共享相同記憶體的時候, 這時要保證每個執行緒在每個時刻看到的共享資料是一致的. 如果每個執行緒使用的變數都是其他執行緒不會使用的(read & write), 或者變數是唯讀的, 就不存在一致性問題. 但是, 如果兩個或兩個以上的執行緒可以read/write乙個變數時, 就需要對執行緒進行同步, 以確保它們在訪問該變數時, 不會得到無效的值, 同時也可以唯一地修改該變數並使它生效.

以上就是我們所說的執行緒同步.

執行緒同步有三種常用的機制: 互斥量(mutex) ,讀寫鎖(rwlock)和條件變數(cond).

互斥量有兩種狀態:lock和unlock, 它確保同一時間只有乙個執行緒訪問資料;

讀寫鎖有三種狀態: 讀加鎖, 寫加鎖, 不加鎖, 只有乙個執行緒可以占有寫模式的讀寫鎖, 但是可以有多個執行緒同時占有讀模式的讀寫鎖.

條件變數則給多個執行緒提供了乙個會合的場所, 與互斥量一起使用時, 允許執行緒以無競爭的方式等待特定條件的發生.

互斥量互斥量從本質上說就是一把鎖, 提供對共享資源的保護訪問.

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 *restric attr);int pthread_mutex_destroy(pthread_mutex_t *mutex);標頭檔案: 返回值: 成功則返回0, 出錯則返回錯誤編號.說明: 如果使用預設的屬性初始化互斥量, 只需把attr設為null. 其他值在以後講解.

2. 互斥操作:

對共享資源的訪問, 要對互斥量進行加鎖, 如果互斥量已經上了鎖, 呼叫執行緒會阻塞, 直到互斥量被解鎖. 在完成了對共享資源的訪問後, 要對互斥量進行解鎖.

首先說一下加鎖函式:

標頭檔案: 原型:int pthread_mutex_lock(pthread_mutex_t *mutex);int pthread_mutex_trylock(pthread_mutex_t *mutex);返回值: 成功則返回0, 出錯則返回錯誤編號.說 明: 具體說一下trylock函式, 這個函式是非阻塞呼叫模式, 也就是說, 如果互斥量沒被鎖住, trylock函式將把互斥量加鎖, 並獲得對共享資源的訪問許可權; 如果互斥量被鎖住了, trylock函式將不會阻塞等待而直接返回ebusy, 表示共享資源處於忙狀態.

再說一下解所函式:

標頭檔案: 原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);返回值: 成功則返回0, 出錯則返回錯誤編號.

3. 死鎖:

死鎖主要發生在有多個依賴鎖存在時, 會在乙個執行緒試圖以與另乙個執行緒相反順序鎖住互斥量時發生. 如何避免死鎖是使用互斥量應該格外注意的東西.

總體來講, 有幾個不成文的基本原則:

a: 對共享資源操作前一定要獲得鎖.    b: 完成操作以後一定要釋放鎖.    c: 盡量短時間地占用鎖.    d: 如果有多鎖, 如獲得順序是abc連環扣, 釋放順序也應該是abc.    e: 執行緒錯誤返回時應該釋放它所獲得的鎖. 讀寫鎖

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, 出錯則返回錯誤編號.這3個函式分別實現獲取讀鎖, 獲取寫鎖和釋放鎖的操作. 獲取鎖的兩個函式是阻塞操作, 同樣, 非阻塞的函式為: #include

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);

成功則返回0, 出錯則返回錯誤編號.非阻塞的獲取鎖操作, 如果可以獲取則返回0, 否則返回錯誤的ebusy.

條件變數

條件變數分為兩部分: 條件和變數. 條件本身是由互斥量保護的. 執行緒在改變條件狀態前先要鎖住互斥量.

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 *restric mutex);

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);

成功則返回0, 出錯則返回錯誤編號.

這兩個函式分別是阻塞等待和超時等待.

等待條件函式等待條件變為真, 傳遞給pthread_cond_wait的互斥量對條件進行保護, 呼叫者把鎖住的互斥量傳遞給函式. 函式把呼叫執行緒放到等待條件的執行緒列表上, 然後對互斥量解鎖, 這兩個操作是原子的. 這樣便關閉了條件檢查和執行緒進入休眠狀態等待條件改變這兩個操作之間的時間通道, 這樣執行緒就不會錯過條件的任何變化.

注意:pthread_cond_wait函式內部是先解鎖,等待一段時間後判斷條件是否滿足。若滿足,則再加鎖,執行資料操作;  若不滿足,則繼續等待。當pthread_cond_wait返回時, 互斥量再次被鎖住.

例項:apr_thread_mutex_lock(m_mutex);

while (m_queue.empty())

//執行緒睡眠解鎖,等待喚醒

apr_thread_cond_wait(m_cond,m_mutex);

if(m_queue.empty())

apr_thread_mutex_unlock(m_mutex);

return false;

item = m_queue.front();  ///取頭元素

m_queue.pop_front();     ///彈出頭元素

apr_thread_mutex_unlock(m_mutex);

3. 通知條件:

#include

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

成功則返回0, 出錯則返回錯誤編號.

這兩個函式用於通知執行緒條件已經滿足. 呼叫這兩個函式, 也稱向執行緒或條件傳送訊號.

必須注意, 一定要在改變條件狀態以後再給執行緒傳送訊號.

Linux 多執行緒互斥量互斥

同乙個程序中的多個執行緒共享所在程序的記憶體資源,當多個執行緒在同一時刻同時訪問同一種共享資源時,需要相互協調,以避免出現資料的不一致和覆蓋等問題,執行緒之間的協調和通訊的就叫做執行緒的同步問題,執行緒同步的思路 讓多個執行緒依次訪問共享資源,而不是並行 mutex被建立時可以有初始值,表示mute...

linux執行緒,互斥量,讀寫鎖,條件變數和屏障

1.建立執行緒 pthread create tid,null,func,null 2.id執行緒識別符號 pthread t tid pthread equal tid1,tid2 pthread t pthread self 3.執行緒終止條件 a pthread exit b return c...

C 多執行緒中的互斥量和互斥鎖

c 多執行緒競爭時經常用到互斥量用std mutex,它可以單獨使用,也可以和互斥鎖在一起使用。std mutex單獨使用的話就lock 加鎖,用unlock 解鎖。為了方便的話可以用用互斥鎖lock guard,lock guard會在建立時自動加鎖,並且在作用域結束時自動析構釋放鎖。lock g...