線程的 同步, 發(fā)生在多個(gè)線程共享相同內(nèi)存的時(shí)候, 這時(shí)要保證每個(gè)線程在每個(gè)時(shí)刻看到的共享數(shù)據(jù)是一致的. 如果每個(gè)線程使用的變量都是其他線程不會(huì)使用的(read & write), 或者變量是只讀的, 就不存在一致性問(wèn)題. 但是, 如果兩個(gè)或兩個(gè)以上的線程可以read/write一個(gè)變量時(shí), 就需要對(duì)線程進(jìn)行同步, 以確保它們?cè)谠L問(wèn)該變量時(shí), 不會(huì)得到無(wú)效的值, 同時(shí)也可以唯一地修改該變量并使它生效. 以上就是我們所說(shuō)的線程同步. 線程同步有三種常用的機(jī)制: 互斥量(mutex) ,讀寫(xiě)鎖(rwlock)和條件變量(cond). 互斥量有兩種狀態(tài):lock和unlock, 它確保同一時(shí)間只有一個(gè)線程訪問(wèn)數(shù)據(jù); 讀寫(xiě)鎖有三種狀態(tài): 讀加鎖, 寫(xiě)加鎖, 不加鎖, 只有一個(gè)線程可以占有寫(xiě)模式的讀寫(xiě)鎖, 但是可以有多個(gè)線程同時(shí)占有讀模式的讀寫(xiě)鎖. 條件變量則給多個(gè)線程提供了一個(gè)會(huì)合的場(chǎng)所, 與互斥量一起使用時(shí), 允許線程以無(wú)競(jìng)爭(zhēng)的方式等待特定條件的發(fā)生. 互斥量 互斥量從本質(zhì)上說(shuō)就是一把鎖, 提供對(duì)共享資源的保護(hù)訪問(wèn). 1. 初始化: 在Linux下, 線程的互斥量數(shù)據(jù)類型是pthread_mutex_t. 在使用前, 要對(duì)它進(jìn)行初始化: 對(duì)于靜態(tài)分配的互斥量, 可以把它設(shè)置為PTHREAD_MUTEX_INITIALIZER, 或者調(diào)用pthread_mutex_init. 對(duì)于動(dòng)態(tài)分配的互斥量, 在申請(qǐng)內(nèi)存(malloc)之后, 通過(guò)pthread_mutex_init進(jìn)行初始化, 并且在釋放內(nèi)存(free)前需要調(diào)用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);
頭文件: <pthread.h>
返回值: 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào).
說(shuō)明: 如果使用默認(rèn)的屬性初始化互斥量, 只需把a(bǔ)ttr設(shè)為NULL. 其他值在以后講解.
2. 互斥操作: 對(duì)共享資源的訪問(wèn), 要對(duì)互斥量進(jìn)行加鎖, 如果互斥量已經(jīng)上了鎖, 調(diào)用線程會(huì)阻塞, 直到互斥量被解鎖. 在完成了對(duì)共享資源的訪問(wèn)后, 要對(duì)互斥量進(jìn)行解鎖. 首先說(shuō)一下加鎖函數(shù): 頭文件: <pthread.h>
原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
返回值: 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào).
說(shuō) 明: 具體說(shuō)一下trylock函數(shù), 這個(gè)函數(shù)是非阻塞調(diào)用模式, 也就是說(shuō), 如果互斥量沒(méi)被鎖住, trylock函數(shù)將把互斥量加鎖, 并獲得對(duì)共享資源的訪問(wèn)權(quán)限; 如果互斥量被鎖住了, trylock函數(shù)將不會(huì)阻塞等待而直接返回EBUSY, 表示共享資源處于忙狀態(tài).
再說(shuō)一下解所函數(shù): 頭文件: <pthread.h>
原型:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值: 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào).
3. 死鎖: 死鎖主要發(fā)生在有多個(gè)依賴鎖存在時(shí), 會(huì)在一個(gè)線程試圖以與另一個(gè)線程相反順序鎖住互斥量時(shí)發(fā)生. 如何避免死鎖是使用互斥量應(yīng)該格外注意的東西. 總體來(lái)講, 有幾個(gè)不成文的基本原則: a: 對(duì)共享資源操作前一定要獲得鎖.
b: 完成操作以后一定要釋放鎖.
c: 盡量短時(shí)間地占用鎖.
d: 如果有多鎖, 如獲得順序是ABC連環(huán)扣, 釋放順序也應(yīng)該是ABC.
e: 線程錯(cuò)誤返回時(shí)應(yīng)該釋放它所獲得的鎖.
讀寫(xiě)鎖
在線程同步系列的第一篇文章里已經(jīng)說(shuō)過(guò), 讀寫(xiě)鎖是因?yàn)橛?種狀態(tài), 所以可以有更高的并行性. 1. 特性: 一次只有一個(gè)線程可以占有寫(xiě)模式的讀寫(xiě)鎖, 但是可以有多個(gè)線程同時(shí)占有讀模式的讀寫(xiě)鎖. 正是因?yàn)檫@個(gè)特性,當(dāng)讀寫(xiě)鎖是寫(xiě)加鎖狀態(tài)時(shí), 在這個(gè)鎖被解鎖之前, 所有試圖對(duì)這個(gè)鎖加鎖的線程都會(huì)被阻塞.當(dāng)讀寫(xiě)鎖在讀加鎖狀態(tài)時(shí), 所有試圖以讀模式對(duì)它進(jìn)行加鎖的線程都可以得到訪問(wèn)權(quán), 但是如果線程希望以寫(xiě)模式對(duì)此鎖進(jìn)行加鎖, 它必須阻塞直到所有的線程釋放鎖. 通常, 當(dāng)讀寫(xiě)鎖處于讀模式鎖住狀態(tài)時(shí), 如果有另外線程試圖以寫(xiě)模式加鎖, 讀寫(xiě)鎖通常會(huì)阻塞隨后的讀模式鎖請(qǐng)求, 這樣可以避免讀模式鎖長(zhǎng)期占用, 而等待的寫(xiě)模式鎖請(qǐng)求長(zhǎng)期阻塞. 2. 適用性: 讀寫(xiě)鎖適合于對(duì)數(shù)據(jù)結(jié)構(gòu)的讀次數(shù)比寫(xiě)次數(shù)多得多的情況. 因?yàn)? 讀模式鎖定時(shí)可以共享, 以寫(xiě)模式鎖住時(shí)意味著獨(dú)占, 所以讀寫(xiě)鎖又叫共享-獨(dú)占鎖. 3. 初始化和銷毀: #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); 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào). 同互斥量一樣, 在釋放讀寫(xiě)鎖占用的內(nèi)存之前, 需要先通過(guò)pthread_rwlock_destroy對(duì)讀寫(xiě)鎖進(jìn)行清理工作, 釋放由init分配的資源.
4. 讀和寫(xiě): #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); 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào). 這3個(gè)函數(shù)分別實(shí)現(xiàn)獲取讀鎖, 獲取寫(xiě)鎖和釋放鎖的操作. 獲取鎖的兩個(gè)函數(shù)是阻塞操作, 同樣, 非阻塞的函數(shù)為:
#include <pthread.h> int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào). 非阻塞的獲取鎖操作, 如果可以獲取則返回0, 否則返回錯(cuò)誤的EBUSY.
條件變量 條件變量分為兩部分: 條件和變量. 條件本身是由互斥量保護(hù)的. 線程在改變條件狀態(tài)前先要鎖住互斥量. 1. 初始化: 條件變量采用的數(shù)據(jù)類型是pthread_cond_t, 在使用之前必須要進(jìn)行初始化, 這包括兩種方式: 靜態(tài): 可以把常量PTHREAD_COND_INITIALIZER給靜態(tài)分配的條件變量.
動(dòng)態(tài): pthread_cond_init函數(shù), 是釋放動(dòng)態(tài)條件變量的內(nèi)存空間之前, 要用pthread_cond_destroy對(duì)其進(jìn)行清理.
#include <pthread.h> int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr); int pthread_cond_destroy(pthread_cond_t *cond); 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào). 當(dāng)pthread_cond_init的attr參數(shù)為NULL時(shí), 會(huì)創(chuàng)建一個(gè)默認(rèn)屬性的條件變量; 非默認(rèn)情況以后討論. 2. 等待條件: #include <pthread.h> 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, 出錯(cuò)則返回錯(cuò)誤編號(hào). 這兩個(gè)函數(shù)分別是阻塞等待和超時(shí)等待. 等待條件函數(shù)等待條件變?yōu)檎? 傳遞給pthread_cond_wait的互斥量對(duì)條件進(jìn)行保護(hù), 調(diào)用者把鎖住的互斥量傳遞給函數(shù). 函數(shù)把調(diào)用線程放到等待條件的線程列表上, 然后對(duì)互斥量解鎖, 這兩個(gè)操作是原子的. 這樣便關(guān)閉了條件檢查和線程進(jìn)入休眠狀態(tài)等待條件改變這兩個(gè)操作之間的時(shí)間通道, 這樣線程就不會(huì)錯(cuò)過(guò)條件的任何變化. 注意:pthread_cond_wait函數(shù)內(nèi)部是先解鎖,等待一段時(shí)間后判斷條件是否滿足。若滿足,,則再加鎖,執(zhí)行數(shù)據(jù)操作; 若不滿足,則繼續(xù)等待,。當(dāng)pthread_cond_wait返回時(shí), 互斥量再次被鎖住.
實(shí)例: apr_thread_mutex_lock(m_mutex);
3. 通知條件: #include <pthread.h> int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 成功則返回0, 出錯(cuò)則返回錯(cuò)誤編號(hào). 這兩個(gè)函數(shù)用于通知線程條件已經(jīng)滿足. 調(diào)用這兩個(gè)函數(shù), 也稱向線程或條件發(fā)送信號(hào). 必須注意, 一定要在改變條件狀態(tài)以后再給線程發(fā)送信號(hào). |
|
來(lái)自: changqiong0606 > 《線程》