1. 程式人生 > >例說讀者寫者模型

例說讀者寫者模型

什麼是讀者寫者模型

讀者和寫者模型是作業系統中的一種同步與互斥機制,它與消費者和生產者模型類似,但也有不同的地方,最明顯的一個特點是在讀者寫者模型中,多個多者之間可以共享“倉庫”,讀者與讀者之間採用了並行機制;而在消費者和生產者模型中,消費者只能有一個獨佔倉庫,消費者與消費者是競爭關係。下圖展示了讀者與寫者模型:
(讀者寫者模型)

讀者寫者模型的要具有的條件

  1. 寫者是排它性的,即在有多個寫者的情況下,只能有一個寫者佔有“倉庫”;
  2. 讀者的並行機制:可以執行多個讀者去訪問倉庫;
  3. 如果讀者佔有了倉庫,那麼寫者則不能佔有;

讀者寫者模型的關係

  1. 讀者優先:讀者先來讀取資料,此時寫者處於阻塞狀態,當讀者都讀取完資料後且沒有讀者了時寫者才能訪問倉庫;
  2. 寫者優先:類似與讀者優先的情況;
  3. 公平情況:寫者與讀者訪問“倉庫”優先順序相等,誰先進入優先順序佇列誰先訪問;

讀寫鎖

什麼是讀寫鎖

讀寫鎖與互斥量類似,不過讀寫鎖允許更高的並行性。它有三種狀態:讀模式下加鎖狀態、寫模式下加鎖狀態、不加鎖狀態,另外,一次只有一個執行緒可以佔用寫模式下的讀寫鎖,但是可以有多個執行緒可以佔有讀模式下的讀寫鎖。

讀寫鎖狀態

  1. 寫加鎖狀態:當處於寫加鎖狀態時,在被解鎖之前,所有試圖對鎖進行訪問的進行都會被阻塞;
  2. 讀加鎖狀態:當處於讀加鎖狀態時,所有試圖以讀模式對這個鎖進行訪問的執行緒都可以獲得訪問權,但是任何以寫模式對此鎖進行訪問的執行緒都會被阻塞,直到所有的執行緒都釋放他們的讀狀態鎖為止。

注意:當讀寫鎖處於讀模式加鎖的狀態時,這時有一個執行緒試圖以寫模式來獲取鎖,讀寫鎖通常會阻塞隨後的讀模式鎖請求,這樣做事為了避免讀模式鎖長期佔用資源,導致寫模式飢餓。

讀寫鎖的介面

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");
}