一、概念
一把讀寫鎖具備三種狀態:
- 讀模式下加鎖狀態(讀鎖)
- 寫模式下加鎖轉態(寫鎖)
- 不加鎖狀態
2. 讀寫鎖特性:
- 讀寫鎖是寫模式加鎖時,解鎖前,所有對該鎖加鎖的線程都會阻塞。
- 讀寫鎖是讀模式加鎖時,如果線程以讀模式加鎖會成功,如果線程以寫模式加鎖會阻塞
- 讀寫鎖是讀模式加鎖時,既有試圖以寫模式加鎖的線程,也有試圖以讀模式加鎖的線程,那么讀模式會讀阻塞隨后的讀模式鎖請求,優先滿足寫模式鎖,讀鎖、寫鎖并行阻塞,寫鎖優先級高。
讀寫鎖也叫共享_獨占鎖,當讀寫鎖以讀模式鎖住時,它以共享模式鎖住;當它以寫模式鎖住時,它是獨占鎖住的,寫獨占,讀共享。
3. 讀寫鎖的特性:
線程A加讀鎖成功, 又來了三個線程, 做讀操作, 可以加鎖成功
- 讀共享 - 并行處理
線程A加寫鎖成功, 又來了三個線程, 做讀操作, 三個線程阻塞
- 寫獨占
線程A加讀鎖成功, 又來了B線程加寫鎖阻塞, 又來了C線程加讀鎖阻塞
- 讀寫不能同時
- 寫的優先級高
4. 讀寫鎖場景:
線程A加寫鎖成功, 線程B請求讀鎖
- 線程B阻塞
線程A持有讀鎖, 線程B請求寫鎖
- 線程B阻塞
線程A擁有讀鎖, 線程B請求讀鎖
- 線程B加鎖成功
線程A持有讀鎖, 然后線程B請求寫鎖, 然后線程C請求讀鎖
- B阻塞,c阻塞 - 寫的優先級高
- A解鎖,B線程加寫鎖成功,C繼續阻塞
- B解鎖,C加讀鎖成功
線程A持有寫鎖, 然后線程B請求讀鎖, 然后線程C請求寫鎖
- BC阻塞
- A解鎖,C加寫鎖成功,B繼續阻塞
- C解鎖,B加讀鎖成功
二、主要函數
1. 函數原型:
#include <pthread.h>
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); 初始化讀寫鎖
pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 銷毀讀寫鎖
2. 函數原型:阻塞版本
#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); 解鎖
?
三、程序清單
1. 測試代碼:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>int counter; //全局資源
pthread_rwlock_t rwlock;void *th_write(void *arg)
{int t;int i = (int)arg;while (1) {t = counter;usleep(1000);pthread_rwlock_wrlock(&rwlock);printf("=======write %d: %lu: counter = %d ++counter = %d\n", i, pthread_self(), t, ++counter);pthread_rwlock_unlock(&rwlock);usleep(5000);}return NULL;
}void *th_read(void *arg)
{int t;int i = (int)arg;while (1) {pthread_rwlock_rdlock(&rwlock);printf("---------------read %d: %lu: %d\n", i, pthread_self(), counter);pthread_rwlock_unlock(&rwlock);usleep(900);}return NULL;
}int main()
{int i;pthread_t tid[8];pthread_rwlock_init(&rwlock, NULL);for (i = 0; i < 3; ++i)pthread_create(&tid[i], NULL, th_write, (void*)i);for (i = 0; i < 5; ++i)pthread_create(&tid[i + 3], NULL, th_read, (void*)i);for (i = 0; i < 8; ++i)pthread_join(tid[i], NULL);pthread_rwlock_destroy(&rwlock);return 0;
}
輸出結果:
?
互斥鎖實現讀寫鎖
?
#include<pthread.h>pthread_mutex_t rdLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t wrLock = PTHREAD_MUTEX_INITIALIZER;
int readCnt = 0;//設置讀鎖調用次數的標志位//實現讀鎖(共享鎖)
void rdLock()
{pthread_mutex_lock(&rdLock);readCnt++;if (readCnt == 1)//有人讀,于是阻塞寫鎖pthread_mutex_lock(&wrLock);pthread_mutex_unlock(&rdLock);
}void rdUnlock()
{pthread_mutex_lock(&rdLock);readCnt--;if (readCnt == 0)//表示已沒有人在讀,釋放寫鎖,可以寫入了pthread_mutex_unlock(&wrLock);pthread_mutex_unlock(&rdLock);
}void wrLock()
{pthread_mutex_lock(&wrLcok);
}void wrUnlock()
{pthread_mutex_unlock(&wrLock);
}
一、實驗項目
【問題描述】程序 trainticket 中,有 100 個線程,其中 90 個線程是查余票數量的,只有 10 個線程搶票,每個線程一次買 10 張票。初始狀態下一共有 1000 張票。因此執行完畢后,還會剩下 900 張票。
程序 trainticket 在運行的時候需要傳入參數,即:
- 參數 0:表示不加任何鎖
- 參數 1:表示使用讀寫鎖
- 參數 2:表示使用互斥量
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>struct Ticket
{int remain; // 余票數,初始化為 1000pthread_rwlock_t rwlock; // 讀寫鎖pthread_mutex_t mlock; // 互斥鎖,主要是為了和讀寫鎖進行對比
}ticket;// 通過命令行傳參數來取得這個值,用來控制到底使用哪一種鎖
// 0:不加鎖 1:加讀寫鎖 2:加互斥鎖
int lock = 0;void* query(void* arg) //查票線程
{int name = (int)arg;sleep(rand() % 5 + 1);if (lock == 1)pthread_rwlock_rdlock(&ticket.rwlock); // 讀模式加鎖else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;sleep(1);printf("%03d query: %d\n", name, remain);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);return NULL;
}void* buy(void* arg) // 搶票線程
{int name = (int)arg;if (lock == 1)pthread_rwlock_wrlock(&ticket.rwlock); // 寫模式加鎖else if (lock == 2)pthread_mutex_lock(&ticket.mlock);int remain = ticket.remain;remain -= 10; // 一次買 10 張票sleep(1);ticket.remain = remain;printf("%03d buy 10 tickets\n", name);if (lock == 1)pthread_rwlock_unlock(&ticket.rwlock);else if (lock == 2)pthread_mutex_unlock(&ticket.mlock);sleep(rand() % 5 + 2);return NULL;
}int main(int argc, char* argv[])
{lock = 0;if (argc >= 2) lock = atoi(argv[1]);int names[100];pthread_t tid[100];int i;for (i = 0; i < 100; ++i) names[i] = i;ticket.remain = 1000;printf("remain ticket = %d\n", ticket.remain);pthread_rwlock_init(&ticket.rwlock, NULL);pthread_mutex_init(&ticket.mlock, NULL);for (i = 0; i < 100; ++i) {if (i % 10 == 0)pthread_create(&tid[i], NULL, buy, (void*)names[i]);elsepthread_create(&tid[i], NULL, query, (void*)names[i]);}for (i = 0; i < 100; ++i) pthread_join(tid[i], NULL);pthread_rwlock_destroy(&ticket.rwlock);pthread_mutex_destroy(&ticket.mlock);printf("remain ticket = %d\n", ticket.remain);return 0;
}
?