00. 目錄
01. 讀寫鎖概述
當有一個線程已經持有互斥鎖時,互斥鎖將所有試圖進入臨界區的線程都阻塞住。但是考慮一種情形,當前持有互斥鎖的線程只是要讀訪問共享資源,而同時有其它幾個線程也想讀取這個共享資源,但是由於互斥鎖的排它性,所有其它線程都無法獲取鎖,也就無法讀訪問共享資源了,但是實際上多個線程同時讀訪問共享資源並不會導致問題。
在對數據的讀寫操作中,更多的是讀操作,寫操作較少,例如對數據庫數據的讀寫應用。爲了滿足當前能夠允許多個讀出,但只允許一個寫入的需求,線程提供了讀寫鎖來實現。
讀寫鎖的特點如下:
1)如果有其它線程讀數據,則允許其它線程執行讀操作,但不允許寫操作。
2)如果有其它線程寫數據,則其它線程都不允許讀、寫操作。
讀寫鎖分爲讀鎖和寫鎖,規則如下:
1)如果某線程申請了讀鎖,其它線程可以再申請讀鎖,但不能申請寫鎖。
2)如果某線程申請了寫鎖,其它線程不能申請讀鎖,也不能申請寫鎖。
POSIX 定義的讀寫鎖的數據類型是: pthread_rwlock_t。
02. 讀寫鎖函數
2.1 讀寫鎖初始化
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
功能:
用來初始化 rwlock 所指向的讀寫鎖。
參數:
rwlock:指向要初始化的讀寫鎖指針。
attr:讀寫鎖的屬性指針。如果 attr 爲 NULL 則會使用默認的屬性初始化讀寫鎖,否則使用指定的 attr
初始化讀寫鎖。
可以使用宏 PTHREAD_RWLOCK_INITIALIZER 靜態初始化讀寫鎖,比如:
pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
這種方法等價於使用 NULL 指定的 attr 參數調用 pthread_rwlock_init() 來完成動態初始化,
不同之處在於PTHREAD_RWLOCK_INITIALIZER 宏不進行錯誤檢查。
返回值:
成功:0,讀寫鎖的狀態將成爲已初始化和已解鎖。
失敗:非 0 錯誤碼。
2.2 讀寫鎖銷燬
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:
用於銷燬一個讀寫鎖,並釋放所有相關聯的資源(所謂的所有指的是由 pthread_rwlock_init() 自動
申請的資源) 。
參數:
rwlock:讀寫鎖指針。
返回值:
成功:0
失敗:非 0 錯誤碼
2.3 申請讀鎖
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
功能:
以阻塞方式在讀寫鎖上獲取讀鎖(讀鎖定)。
如果沒有寫者持有該鎖,並且沒有寫者阻塞在該鎖上,則調用線程會獲取讀鎖。
如果調用線程未獲取讀鎖,則它將阻塞直到它獲取了該鎖。一個線程可以在一個讀寫鎖上多次執行讀鎖定。
線程可以成功調用 pthread_rwlock_rdlock() 函數 n 次,但是之後該線程必須調用
pthread_rwlock_unlock() 函數 n 次才能解除鎖定。
參數:
rwlock:讀寫鎖指針。
返回值:
成功:0
失敗:非 0 錯誤碼
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
用於嘗試以非阻塞的方式來在讀寫鎖上獲取讀鎖。
如果有任何的寫者持有該鎖或有寫者阻塞在該讀寫鎖上,則立即失敗返回。
2.4 申請寫鎖
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
功能:
在讀寫鎖上獲取寫鎖(寫鎖定)。
如果沒有寫者持有該鎖,並且沒有寫者讀者持有該鎖,則調用線程會獲取寫鎖。
如果調用線程未獲取寫鎖,則它將阻塞直到它獲取了該鎖。
參數:
rwlock:讀寫鎖指針。
返回值:
成功:0
失敗:非 0 錯誤碼
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
用於嘗試以非阻塞的方式來在讀寫鎖上獲取寫鎖。
如果有任何的讀者或寫者持有該鎖,則立即失敗返回。
2.5 釋放讀寫鎖
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
功能:
無論是讀鎖或寫鎖,都可以通過此函數解鎖。
參數:
rwlock:讀寫鎖指針。
返回值:
成功:0
失敗:非 0 錯誤碼
03. 讀寫鎖應用示例
下面是一個使用讀寫鎖來實現 4 個線程讀寫一段數據是實例。在此示例程序中,共創建了 4 個線程,其中兩個線程用來寫入數據,兩個線程用來讀取數據。當某個線程讀操作時,其他線程允許讀操作,卻不允許寫操作;當某個線程寫操作時,其它線程都不允許讀或寫操作。
參考代碼如下:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_rwlock_t rwlock; //讀寫鎖
int num = 1;
//讀操作,其他線程允許讀操作,卻不允許寫操作
void *fun1(void *arg)
{
while(1)
{
pthread_rwlock_rdlock(&rwlock);
printf("read num first===%d\n",num);
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
//讀操作,其他線程允許讀操作,卻不允許寫操作
void *fun2(void *arg)
{
while(1)
{
pthread_rwlock_rdlock(&rwlock);
printf("read num second===%d\n",num);
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
}
//寫操作,其它線程都不允許讀或寫操作
void *fun3(void *arg)
{
while(1)
{
pthread_rwlock_wrlock(&rwlock);
num++;
printf("write thread first\n");
pthread_rwlock_unlock(&rwlock);
sleep(2);
}
}
//寫操作,其它線程都不允許讀或寫操作
void *fun4(void *arg)
{
while(1)
{
pthread_rwlock_wrlock(&rwlock);
num++;
printf("write thread second\n");
pthread_rwlock_unlock(&rwlock);
sleep(1);
}
}
int main()
{
pthread_t ptd1, ptd2, ptd3, ptd4;
pthread_rwlock_init(&rwlock, NULL);//初始化一個讀寫鎖
//創建線程
pthread_create(&ptd1, NULL, fun1, NULL);
pthread_create(&ptd2, NULL, fun2, NULL);
pthread_create(&ptd3, NULL, fun3, NULL);
pthread_create(&ptd4, NULL, fun4, NULL);
//等待線程結束,回收其資源
pthread_join(ptd1,NULL);
pthread_join(ptd2,NULL);
pthread_join(ptd3,NULL);
pthread_join(ptd4,NULL);
pthread_rwlock_destroy(&rwlock);//銷燬讀寫鎖
return 0;
}
測試結果:
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out
read num first===1
read num second===1
write thread first
write thread second
write thread second
read num first===4
read num second===4
write thread first
write thread second
read num first===6
read num first===6
write thread second
read num second===7
write thread first
read num first===8
write thread second
write thread second
read num first===10
read num second===10
write thread first
read num first===11
write thread second
write thread second
read num first===13
read num second===13
write thread first
write thread second
read num first===15
read num first===15
write thread second