例說讀者寫者模型
什麼是讀者寫者模型
讀者和寫者模型是作業系統中的一種同步與互斥機制,它與消費者和生產者模型類似,但也有不同的地方,最明顯的一個特點是在讀者寫者模型中,多個多者之間可以共享“倉庫”,讀者與讀者之間採用了並行機制;而在消費者和生產者模型中,消費者只能有一個獨佔倉庫,消費者與消費者是競爭關係。下圖展示了讀者與寫者模型:
讀者寫者模型的要具有的條件
- 寫者是排它性的,即在有多個寫者的情況下,只能有一個寫者佔有“倉庫”;
- 讀者的並行機制:可以執行多個讀者去訪問倉庫;
- 如果讀者佔有了倉庫,那麼寫者則不能佔有;
讀者寫者模型的關係
- 讀者優先:讀者先來讀取資料,此時寫者處於阻塞狀態,當讀者都讀取完資料後且沒有讀者了時寫者才能訪問倉庫;
- 寫者優先:類似與讀者優先的情況;
- 公平情況:寫者與讀者訪問“倉庫”優先順序相等,誰先進入優先順序佇列誰先訪問;
讀寫鎖
什麼是讀寫鎖
讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。它有三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態、不加鎖狀態,另外,一次只有一個執行緒可以佔用寫模式下的讀寫鎖,但是可以有多個執行緒可以佔有讀模式下的讀寫鎖。
讀寫鎖狀態
- 寫加鎖狀態:當處於寫加鎖狀態時,在被解鎖之前,所有試圖對鎖進行訪問的進行都會被阻塞;
- 讀加鎖狀態:當處於讀加鎖狀態時,所有試圖以讀模式對這個鎖進行訪問的執行緒都可以獲得訪問權,但是任何以寫模式對此鎖進行訪問的執行緒都會被阻塞,直到所有的執行緒都釋放他們的讀狀態鎖為止。
注意:當讀寫鎖處於讀模式加鎖的狀態時,這時有一個執行緒試圖以寫模式來獲取鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求,這樣做事為了避免讀模式鎖長期佔用資源,導致寫模式飢餓。
讀寫鎖的介面
1. 讀寫鎖的型別
在Linux系統下,讀寫鎖被定義為pthread_rwlock_t型別,但是其本質上還是一個記憶體計數器。
2. 讀寫鎖的初始化
使用pthread_rwlock_init函式進行初始化;
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
//return val: success return zero
引數attr表示的是初始化讀寫鎖的屬性,如果想使用預設屬性,直接將該引數設定為NULL即可。
另外在single UNIX Specification中,在XSI還擴充套件了用PTHREAD_RWLOCK_INITIALIZER巨集來進行初始化。
3. 讀寫鎖的銷燬
在釋放讀寫鎖的記憶體前,需要呼叫pthread_rwlock_destroy函式來對讀寫鎖進行清理工作,在清理函式中一般是釋放初始化函式對讀寫鎖分配的資源。
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,失敗返回錯誤碼。
相關案例
為了演示本文中提到的讀者和寫者模型,我做了下面的測試案例,使用執行緒來模擬讀者和寫者,使用連結串列來模擬讀者和寫著交換資料的倉庫,寫者負責向連結串列中新增資料,而讀者負責從連結串列中讀取資料,我們來看看使用讀寫鎖後的測試結果:
下面給出案例程式碼:
//讀者和寫者場景模擬程式碼
/*************************************************************************
> File Name: Read_Write.c
> Author: LZH
> Mail: [email protected]
> Created Time: Sun 19 Feb 2017 10:56:59 PM PST
************************************************************************/
#include "myList.h"
#include<pthread.h>
//int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
//int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t lock;
void* Pthread_Write(void* arg)
{
Node_p head=(Node_p)arg;
while(1)
{
sleep(1);
int ret=pthread_rwlock_wrlock(&lock);
if(ret!=0){
perror("wrlock error..\n");
return (void*) -1;
}
int data1=rand()%1000;
int data2=rand()%1000;
PushHead(head,data1);
PushHead(head,data2);
sleep(1);
printf("Writer write data:%d,%d to list.\n",data1,data2);
pthread_rwlock_unlock(&lock);
}
return (void*) 0;
}
void* Pthread_Read1(void* arg)
{
Node_p head=(Node_p)arg;
while(1)
{
sleep(1);
int ret=pthread_rwlock_rdlock(&lock);
if(ret!=0){
perror("rdlock error..\n");
return (void*) -1;
}
int data=0;
PopHead(head,&data);
sleep(1);
printf("Reader1 receive data:%d from list.\n",data);
pthread_rwlock_unlock(&lock);
}
return (void*) 0;
}
void* Pthread_Read2(void* arg)
{
Node_p head=(Node_p)arg;
while(1)
{
sleep(1);
int ret=pthread_rwlock_rdlock(&lock);
if(ret!=0){
perror("rdlock error..\n");
return (void*) -1;
}
int data=0;
PopHead(head,&data);
sleep(1);
printf("Reader2 receive data:%d from list.\n",data);
pthread_rwlock_unlock(&lock);
}
return (void*) 0;
}
int main()
{
printf("Read and Write model...\n");
Node_p head;
ListInit(&head);
printf("lock:%d\n",lock);
pthread_t tid1,tid2,tid3;
pthread_rwlock_init(&lock,NULL);
pthread_create(&tid1,NULL,Pthread_Write,(void*)head);
pthread_create(&tid2,NULL,Pthread_Read1,(void*)head);
pthread_create(&tid3,NULL,Pthread_Read2,(void*)head);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_join(tid3,NULL);
pthread_rwlock_destroy(&lock);
return 0;
}
//連結串列原始檔
#include "myList.h"
Node_p AllocNode(int data)
{
Node_p NewNode=(Node_p)malloc(sizeof(Node));
if(NewNode==NULL)
{
perror("malloc..\n");
return ;
}
NewNode->data=data;
NewNode->next=NULL;
return NewNode;
}
int IsEmpty(Node_p list)
{
assert(list);
if(list->next!=NULL)
{
return 0;
}
else
{
return 1;
}
}
void ListInit(Node_pp head)
{
*head=AllocNode(0);
}
void PushHead(Node_p list,int data)
{
assert(list);
Node_p NewNode=AllocNode(data);
NewNode->next=list->next;
list->next=NewNode;
}
void DelNode(Node_p node)
{
assert(node);
free(node);
node=NULL;
}
void PopHead(Node_p list,int *data)
{
assert(data);
if(IsEmpty(list))
{
printf("the list empty..\n");
return;
}
Node_p dNode=list->next;
list->next=dNode->next;
*data=dNode->data;
DelNode(dNode);
}
void ShowList(Node_p list)
{
assert(list);
Node_p cur=list->next;
while(cur)
{
printf("%d ",cur->data);
cur=cur->next;
}
printf("\n");
}
void DestroyList(Node_p list)
{
assert(list);
int data=0;
while(list->next)
{
PopHead(list,&data);
}
free(list);
list=NULL;
printf("list is destroy...\n");
}