读者写者模型

本文介绍了读者写者模型,这是一种同步问题的解决方案,强调了读者与写者之间的三种关系,以及它与生产者消费者模型的区别。文章还讨论了自旋锁与挂起等待锁的选择,并介绍了读写锁的概念,它是提高并发性的有效工具。最后,提供了读写锁的接口使用示例以及并发编程中的Makefile和源代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是读者写者模型?

同上篇博客中提到的生产者消费者模型类似,我们可以把读者写者模型简单的概括为“三二一”原则

所谓的“三”指的是三种关系,分别为:

(1)写者与写者:互斥关系
(2)读者与读者:既不同步也不互斥
(3)读者与写者:同步关系(读者优先或者写者优先),互斥关系(读者读完了写者才能写,写者写完了读者才能读,这样保证了数据的有效性)

所谓的“二”指的是:读者和写者

所谓的“一”指的是:一个读写场所

二、读者写者模型与生产者与消费者模型的区别

我们可以将生产者当做是读写模型中的写者,消费者当做是读写模型中的读者。

但是在生产者与消费者模型中,消费者与消费者的关系是互斥的,而对于我们的读写模型中的读者与读者是没关系的。

这两种模型的最大区别是:在生产者消费者模型中,生产者产生数据,消费者必须把数据拿走,才算是消费成功了。但是对于我们的读写模型来说,读者只需读到数据即可,它不需要拿走数据。

三、自旋锁与挂起等待锁

挂起等待锁:当线程申请资源失败时,它会挂起等待(操作系统将它挂起)。比如互斥锁和二元信号量。

自旋锁:当线程申请资源失败时,它会一直在当前检测锁位置处轮寻锁的状态来看资源是否能被申请。不断检测的过程叫作自旋(操作系统将它自旋)。

那么如何来选择这两种类型的锁呢?我们来根据线程在临界区需要等待的时间长短来进行选择,需要在临界区等待时间长的线程选择挂起等待锁,而等待时间段的线程选择自旋锁。

了解以上内容我们就来说说在读写模型中最重要的一个知识点——读写锁

读写锁是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者,但不能同时既有读者又有写者。

上述文字中提到了并发这个概念,那么什么是并发呢?并发是一段时间内因为CPU切换导致的若干线程同时运行的现象叫作并发。它是通过线程或进程的来回切换实现的。

接下来我们来看看读写锁的具体接口吧!

(1)创建与销毁读写锁——pthread_rwlock_init和pthread_rwlock_destroy

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,  const pthread_rwlockattr_t *restrict attr)//创建读写锁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);//销毁读写锁

(2)加读写锁

读者加锁——pthread_rwlock_tryrdlock

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);//阻塞版本
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);//非阻塞版本。成功返回值为0,失败返回错误码

写者加锁——pthread_rwlock_trywrlock

 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);//非阻塞版本
 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);//阻塞版本

(3)解锁——pthread_rwlock_unlock

 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

好啦!知道了上述所有的背景知识后,我们来看看具体实现读写模型的具体代码吧!
(1)创建Makefile

myrw:myrw.c
    gcc -o $@ $^ -lpthread
.PHONY:clean
clean:
    rm -f myrw

(2)创建myrw.c

#include<stdio.h>
#include<pthread.h>

int book=0;//给book中写数据
pthread_rwlock_t rwlock;//定义读写锁

void *myread(void *arg)//读者
{   

    while(1)
    {  
        if(pthread_rwlock_tryrdlock(&rwlock)!=0)//读者加锁失败,则说明写者正在写
        {
            printf("reader say:the wirter is writing...\n");

        }
        else
        {
        printf("read done...%d\n",book);
        sleep(5);
        pthread_rwlock_unlock(&rwlock);//解锁

        }
    }

}
void *mywirte(void *arg)//写者
{
     sleep(1);  //读者先运行
     while(1)
     {  
         if(pthread_rwlock_trywrlock(&rwlock)!=0)//写者加锁失败,则说明读者正在读
        {
           printf("writer say:the reader is reading...\n");
           usleep(500000);//0.5s刷新一次
        }
        else
        {
         book++;
         printf("write done...%d\n",book);
         pthread_rwlock_unlock(&rwlock);

        }
     }

}

int main()
{
   pthread_rwlock_init(&rwlock,NULL);//初始化读写锁

   pthread_t reader,wirter;

   pthread_create(&reader,NULL,myread,NULL);
   pthread_create(&wirter,NULL,mywirte,NULL);

   pthread_join(reader,NULL);
   pthread_join(wirter,NULL);

   pthread_rwlock_destroy(&rwlock);//销毁读写锁

   return 0;
}

以上的代码是读者先读的情况!程序运行成功的结果如图:

这里写图片描述

写者先写的情况只需将myread与mywirte函数部分做如下修改即可:

void *myread(void *arg)
{   
    sleep(1);//写者先运行
    while(1)
    {  
        if(pthread_rwlock_tryrdlock(&rwlock)!=0)
        {
            printf("reader say:the wirter is writing...\n");
            usleep(500000);
        }
        else
        {
        printf("read done...%d\n",book);

        pthread_rwlock_unlock(&rwlock);

        }
    }

}
void *mywirte(void *arg)
{

     while(1)
     {  
         if(pthread_rwlock_trywrlock(&rwlock)!=0)
        {
           printf("writer say:the reader is reading...\n");

        }
        else
        {
         book++;
         printf("write done...%d\n",book);
         sleep(5);
         pthread_rwlock_unlock(&rwlock);

        }
     }

}

程序成功后结果如图所示:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值