在以前的一篇博文Linux多線程編程初探中,只提到了用於線程同步的互斥鎖、條件變量,而沒有提及讀寫鎖(read-write lock)。
本文主要整理自以下文章:
讀寫鎖(read-write lock)機制-----多線程同步問題的解決
讀寫鎖
讀寫鎖比mutex有更高的適用性,可以多個線程同時占用讀模式的讀寫鎖,但是只能一個線程占用寫模式的讀寫鎖。
1)當讀寫鎖是寫加鎖狀態時, 在這個鎖被解鎖之前, 所有試圖對這個鎖加鎖的線程都會被阻塞.
2)當讀寫鎖在讀加鎖狀態時, 所有試圖以讀模式對它進行加鎖的線程都可以得到訪問權,但是以寫模式對它進行枷鎖的線程將阻塞;
3)當讀寫鎖在讀模式鎖狀態時, 如果有另外線程試圖以寫模式加鎖, 讀寫鎖通常會阻塞隨后的讀模式鎖請求, 這樣可以避免讀模式鎖長期占用, 而等待的寫模式鎖請求長期阻塞;
這種鎖適用對數據結構進行讀的次數比寫的次數多的情況。
讀寫鎖API
初始化和銷毀
對於讀寫鎖變量的初始化可以有兩種方式,一種是通過給一個靜態分配的讀寫鎖賦予常值PTHREAD_RWLOCK_INITIALIZER來初始化它,另一種方法就是通過調用pthread_rwlock_init()來動態的初始化。而當某個線程不再需要讀寫鎖的時候,可以通過調用pthread_rwlock_destroy來銷毀該鎖。函數原型如下:
#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, 出錯則返回錯誤編號.
在釋放某個讀寫鎖占用的內存之前,要先通過pthread_rwlock_destroy對讀寫鎖進行清理,釋放由pthread_rwlock_init所分配的資源。在初始化某個讀寫鎖的時候,如果屬性指針attr是個空指針的話,表示默認的屬性;如果想要使用非默認屬性,則要使用到下面的兩個函數:
#include int pthread_rwlockattr_init(pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy(pthread_rwlockatttr_t *attr); // 成功返回0,出錯則返回錯誤碼。
這里還需要說明的是,當初始化讀寫鎖完畢以后呢,該鎖就處於一個非鎖定狀態。數據類型為pthread_rwlockattr_t的某個屬性對象一旦初始化了,就可以通過不同的函數調用來啟用或者是禁用某個特定的屬性。
讀加鎖和寫加鎖
讀寫鎖的數據類型是pthread_rwlock_t,如果這個數據類型中的某個變量是靜態分配的,那么可以通過給它賦予常值PTHREAD_RWLOCK_INITIALIZAR來初始化它。pthread_rwlock_rdlock()用來獲取讀出鎖,如果相應的讀出鎖已經被某個寫入者占有,那么就阻塞調用線程。pthread_rwlock_wrlock()用來獲取一個寫入鎖,如果相應的寫入鎖已經被其它寫入者或者一個或多個讀出者占有,那么就阻塞該調用線程;pthread_rwlock_unlock()用來釋放一個讀出或者寫入鎖。函數原型如下:
#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, 出錯則返回錯誤編號.
要注意的是其中獲取鎖的兩個函數的操作都是阻塞操作,也就是說獲取不到鎖的話,那么調用線程不是立即返回,而是阻塞執行。有寫情況下,這種阻塞式的獲取所得方式可能不是很適用,所以,接下來引入兩個采用非阻塞方式獲取讀寫鎖的函數pthread_rwlock_tryrdlock()和pthread_rwlock_trywrlock(),非阻塞方式下獲取鎖的時候,如果不能馬上獲取到,就會立即返回一個EBUSY錯誤,而不是把調用線程投入到睡眠等待。函數原型如下:
#include <pthread.h> int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); // 成功則返回0, 出錯則返回錯誤編號.
用互斥鎖實現讀寫鎖
偽代碼如下:
1 count_mutex = mutex_init(); 2 write_mutex = mutex_init(); 3 read_count = 0; 4 5 void read_lock{ 6 lock(count_mutex); 7 read_count++; 8 if (read_count == 1) { 9 lock(write_mutex); 10 } 11 unlock(count_mutex); 12 } 13 14 void read_unlock{ 15 lock(count_mutex); 16 read_count--; 17 if (read_count == 0) { 18 unlock(write_mutex); 19 } 20 unlock(count_mutex); 21 } 22 23 void write_lock{ 24 lock(write_mutex); 25 } 26 27 void write_unlock{ 28 unlock(write_mutex); 29 }
