Linux---讀者寫者模型
一 怎麼理解讀者寫者模型
讀者寫者問題就是在一些程式中,對某些資源的訪問會存在兩種可能的情況:一種就是寫操作,寫操作是可以獨佔資源的,具有排他性;另一種情況就是讀操作,在讀操作中可以有多個操作併發的去訪問某種資源,它的訪問方式是共享的。這種從對檔案的讀寫操作中總結出的的這種模型叫做讀寫者模型。
1.讀者寫者模型的三種關係
(1)讀者和讀者——沒有關係;
(2)讀者和寫者——同步和互斥;
(3)寫者和寫者——互斥。
2.同步和互斥
同步:同步是在互斥的基礎上通過一些機制實現訪問者對資源的訪問是有序的。
互斥:某一種資源同時只能允許一個訪問者對他進行訪問,具有唯一性和排他性,但是僅僅保持互斥關係是沒法保證對某種公共資源的有序訪問的,所以需要互斥和同步配合使用。
二 什麼是讀寫鎖
讀寫鎖實際上是一種特殊的自旋鎖,他把對資源的訪問分為讀者和寫者,讀者只進行對資源的訪問,寫者則需要對共享資源進行寫操作。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與cpu數相關),但是不能既有讀者又有寫者,
讀寫鎖狀態
1.讀加鎖狀態:當處於讀加鎖狀態時,所有試圖以讀模式對這個鎖進行訪問的執行緒都可以獲得訪問權,但是任何以寫模式對此鎖進行訪問的執行緒都會被阻塞,直到所有的執行緒都釋放他們的讀狀態鎖為止。
2.寫加鎖狀態:當處於寫加鎖狀態時,在被解鎖之前,所有試圖對鎖進行的訪問都會被阻塞;
三 關於讀寫鎖的函式
1)建立讀寫鎖
pthread_rwlock_t myrwlock
2)初始化讀寫鎖
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
成功返回0,失敗返回錯誤碼。attr表示讀寫鎖的屬性,一般設定為NULL表示預設.
3)銷燬讀寫鎖
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
成功返回0,失敗返回操作碼,rwlock就是你定義的讀寫鎖.
4)讀者加鎖函式
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//加鎖失敗則被掛起等待
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//加鎖失敗不掛起等待採用輪詢試等待
兩者都是成功返回0,失敗返回錯誤碼。
)5寫者加鎖函式
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
兩者同讀者加鎖函式,成功返回0, 失敗返回錯誤碼。
6)讀者寫者解鎖函式
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
成功返回0, 失敗返回錯誤碼。
接下來我們來看程式碼
1 #include <stdio.h>
2 #include <pthread.h>
3
4 int count = 0;
5 pthread_rwlock_t myrwlock;
6
7 void *reader_run(void *arg)
8 {
9 while(1)
10 {
11 sleep(1);
12 int ret = pthread_rwlock_rdlock(&myrwlock);
13 if(ret != 0)
14 {
15 printf("writer is writing, reader waiting\n");
16 continue;
17 }
18 printf("reader is %d, count=%d\n", pthread_self(), count);
19 pthread_rwlock_unlock(&myrwlock);
20 }
21 }
22
23 void *writer_run(void *arg)
24 {
25 while(1)
26 {
27 sleep(1);
28 int ret = pthread_rwlock_wrlock(&myrwlock);
29 if(ret != 0)
30 {
31 printf("reader is reading, writer waiting\n");
32 continue;
33 }
34 count++;
35 printf("write is %d, count=%d\n", pthread_self(), count);
36 pthread_rwlock_unlock(&myrwlock);
37 }
38 }
39
40 int main()
41 {
42 pthread_rwlock_init(&myrwlock, NULL);
43 pthread_t reader;
44 pthread_t writer;
45 pthread_create(&reader, NULL, reader_run, NULL);
46 pthread_create(&writer, NULL, writer_run, NULL);
47 pthread_join(reader,NULL);
48 pthread_join(writer, NULL);
49 pthread_rwlock_destroy(&myrwlock);
50 return 0;
51 }
執行結果:
其實讀者和寫者之間並不是一直是公平的讀寫,他們之間一紅有三種情況:
1、讀者優先
讀者先來讀取資料(其他的讀者也可以來讀取資料),此時寫者等待,也就是說讀者可以插寫者的隊,這是讀者優先的關鍵點,只有當讀者為0,寫者才能來寫入資料。寫者有一個互斥訊號量 writeMutex=1,因為寫者一次只能一個來寫資料,
對讀者有一個記錄數目的 int 變數,readcount=0,一個互斥訊號量readMutex = 1,保證多個讀者來的時候,能使 readcount 互斥的變化,也就是不被混亂的計數。
2、寫者優先
類似讀者優先演算法,同理,這裡是寫者可以插隊,多用一個 readable 訊號量,控制寫者可以優先於讀者進入臨界區,一個整數 writecount 統計寫者,而 wmutex 控制寫者互斥訪問 writecount。
3、公平情況
讀者想進的時候,有寫者正在寫(或者正在等待寫),讀者就不能進(讀者等待),只有寫者走了,讀者才能進。和一相比,需要多一個訊號量 wmutex=1,表示是否存在寫者正在寫或者等待寫,如果存在,讀者就等待,讀者不能插隊了。(以上的實驗程式碼就是一種公平的情況)
讀者寫者模型和生產者消費者模型之間的區別
上篇部落格我們也瞭解到了生產者消費者模型,從功能和實現上來說它和這次的讀者寫者模型是有一定類似性的,那麼他們之間有什麼區別呢?
讀者寫者模型中寫者寫入資料,但讀者並不會消費資料,只會訪問。其中的寫操作是排他的(排斥讀者和其他寫者),讀操作是共享的。而生產者消費者模型中生產者生產產品,消費者不斷的消費產品,產品的數量在不斷的下降。如果此時臨界區無資料時,消費者被阻塞,直至生產者生產出資料時來喚醒消費者,若臨界區資源滿了時,生產者被阻塞,在消費者消費後喚醒生產者