Linux:使用讀寫鎖使線程同步
基礎與控制原語
讀寫鎖
與互斥量類似,但讀寫鎖允許更高的並行性。其特性為:寫獨占,讀共享。
讀寫鎖狀態:
一把讀寫鎖具備三種狀態:
1. 讀模式下加鎖狀態 (讀鎖)
2. 寫模式下加鎖狀態 (寫鎖)
3. 不加鎖狀態
讀寫鎖特性:
- 讀寫鎖是"寫模式加鎖"時, 解鎖前,所有對該鎖加鎖的線程都會被阻塞。
- 讀寫鎖是"讀模式加鎖"時, 如果線程以讀模式對其加鎖會成功;如果線程以寫模式加鎖會阻塞。
- 讀寫鎖是"讀模式加鎖"時, 既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程。那麽讀寫鎖會阻塞隨後的讀模式鎖請求。優先滿足寫模式鎖。讀鎖、寫鎖並行阻塞,寫鎖優先級高
讀寫鎖也叫共享-獨占鎖。當讀寫鎖以讀模式鎖住時,它是以共享模式鎖住的;當它以寫模式鎖住時,它是以獨占模式鎖住的。寫獨占、讀共享。
讀寫鎖非常適合於對數據結構讀的次數遠大於寫的情況。
主要應用函數:
pthread_rwlock_init函數
pthread_rwlock_destroy函數
pthread_rwlock_rdlock函數
pthread_rwlock_wrlock函數
pthread_rwlock_tryrdlock函數
pthread_rwlock_trywrlock函數
pthread_rwlock_unlock函數
以上7 個函數的返回值都是:成功返回0, 失敗直接返回錯誤號。
pthread_rwlock_t類型 用於定義一個讀寫鎖變量。
pthread_rwlock_t rwlock;
pthread_rwlock_init函數
初始化一把讀寫鎖
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
參2:attr表讀寫鎖屬性,通常使用默認屬性,傳NULL即可。
pthread_rwlock_destroy
銷毀一把讀寫鎖
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
pthread_rwlock_rdlock函數
以讀方式請求讀寫鎖。(常簡稱為:請求讀鎖)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_wrlock函數
以寫方式請求讀寫鎖。(常簡稱為:請求寫鎖)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
pthread_rwlock_unlock函數
解鎖
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
pthread_rwlock_tryrdlock函數
非阻塞以讀方式請求讀寫鎖(非阻塞請求讀鎖)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
pthread_rwlock_trywrlock函數
非阻塞以寫方式請求讀寫鎖(非阻塞請求寫鎖)
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
讀寫鎖示例
看如下示例,同時有多個線程對同一全局數據讀、寫操作。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_rwlock_t rwlock;
long int love;
void *pth_wr(void *arg)
{
int i = (int)arg;
while (love <= 520)
{
pthread_rwlock_wrlock(&rwlock);//請求寫鎖
printf("write================全局變量love = %ld, 我是%d號線程。\n", love += 40, i + 1);
pthread_rwlock_unlock(&rwlock);//解鎖
sleep(1);
}
return NULL;
}
void *pth_rd(void *arg)
{
int i = (int)arg;
while (love <= 520)
{
pthread_rwlock_rdlock(&rwlock);//請求讀鎖
printf("全局變量love = %ld, 我是%d號線程。————— - read\n", love, i + 1);
pthread_rwlock_unlock(&rwlock);//解鎖
sleep(1);
}
return NULL;
}
int main(void)
{
pthread_t pth[10];
int i;
pthread_rwlock_init(&rwlock, NULL);
for (i = 0; i != 5; i++)//寫
{
pthread_create(&pth[i], NULL, pth_wr, (void *)i);
}
for (i = 0; i != 5; i++)//讀
{
pthread_create(&pth[5 + i], NULL, pth_rd, (void *)i);
}
while (1)
{
if (love >= 520)
{
for (int j = 0; j != 10; j++)
{
pthread_cancel(pth[j]);//殺死線程
pthread_join(pth[j], NULL);//回收線程
}
break;
}
}
return 0;
}
在裏面,定義了一個全局變量love,當love大於520的時候就停止讀寫並殺死回收線程。讀寫鎖加解鎖的位置和互斥量加解鎖的位置一樣,直接看結果吧:看,讀數據的時候時一致的,並且讀出來的數據與前面寫進去的數據保持一致,並沒有出現讀出的數據和寫入的數據不一致的情況。雖然線程執行順序沒有順序,但是這並不重要,這只是內核調度和線程爭奪資源的結果,我們關心的不是線程執行順序,而是結果。很明顯,加了鎖之後整整齊齊,是我們想要的效果;當然這樣寫在64位上編譯的時候會有警告:原因我在上一篇博客上說了的,這裏不再做解釋。
現在我們來看看不加鎖的情況,代碼就是把那些鎖去掉就是。
這是每次加40的結果;
這是每次加20的結果,可以看到,次數越多,混亂的情況就越嚴重。
Linux:使用讀寫鎖使線程同步