【读者写者问题与读写锁】


一、读者写者问题

之前学过的生产消费者模型中,有321原则,也就是:
三种关系,两个角色,一个交易场所。
同样地:
读者写着问题也有321原则:

  • 三种关系:

    • 写者和写者:它们是竞争关系,一个人写的时候,另一个人不能同时也写,所以需要基本的互斥条件。
    • 读者和写者:当读者在读的时候,写者不能写数据,得等读者读完才能写,而当写者在写时,读者也不能立刻读,得等写者写完才能读。所以也需要基本的同步和互斥条件。
    • 读者和读者:读者和读者之间是并发关系,其实也就是没有关系,一个人读完,不影响另一个人读。因为读者并不会取走数据
  • 两个角色:读者和写者

  • 一个交易场所。

读者写者问题vs生产消费模型

它们两个模型最本质的区别在于

读者和读者与消费者和消费者

消费者和消费者之间,消费者会带走数据,注定他们是竞争关系。
而读者和读者之间,不会带走数据!!!

其他没什么区别。

理解读者写者模型的原理

下面给一段伪代码来理解:

公共部分:

uint32_t reader_count = 0;
lock_t count_lock;
lock_t writer_lock;

由于读者之间是并发关系,所以,reader_count是把计数器,是为了统计访问临界资源的读者的个数。而count_lock是为了给计数器上锁的,因为有多个读者,防止同时对计数器修改,要加锁保护。
而读者和写者,写者和写者之间是互斥关系,所以要有一把writer_lock。

Reader

// 加锁
lock(count_lock);
if(reader_count == 0)
	lock(writer_lock);
++reader_count;
unlock(count_lock);

// read;  //访问临界资源

//解锁
lock(count_lock);
--reader_count;
if(reader_count == 0)
	unlock(writer_lock);
unlock(count_lock);

第一个读者进来时,会先申请计数器的锁,并判断计数器资源是否== 0,如果为0,表明我是第一个读者,我会把写者锁申请走,这样当写者想要申请锁来写时,就要阻塞等待锁就绪,相当于防止写者进来了,这样就完成了读者和写者的互斥。然后再对计数器++

当读者读完离开时,会申请计数器的锁,进行计数器的- -,并判断,当前在访问临界资源的读者是否为0,如果为0,就会释放写者锁,让写者能进来写。

Writer

lock(writer_lock);
// write 
unlock(writer_lock);

这里不用解释了,就完成了写者和写者,写者和读者的互斥。

读者优先和写者优先

  • 读者优先(Reader-Preference)
    在这种策略中,系统会尽可能多地允许多个读者同时访问资源(比如共享文件或数据),而不会优先考虑写者。这意味着当有读者正在读取时,新到达的读者会立即被允许进入读取区,而写者则会被阻塞,直到所有读者都离开读取区。读者优先策略可能会导致写者饥饿(即写者长时间无法获得写入权限),特别是当读者频繁到达时。

在这里插入图片描述

  • 写者优先(Writer-Preference)
    在这种策略中,系统会优先考虑写者。当写者请求写入权限时,系统会尽快地让写者进入写入区,即使此时有读者正在读取。这通常意味着一旦有写者到达,所有后续的读者都会被阻塞,直到写者完成写入并离开写入区。写者优先策略可以减少写者等待的时间,但可能会导致读者饥饿(即读者长时间无法获得读取权限),特别是当写者频繁到达时。

要注意的是,不管是读者饥饿问题还是写者饥饿问题,都不是一个弊端,这都只是在不同优先情况下表现出来的特点。

从代码角度理解读者写者模型

读写锁使用的基本接口

在这里插入图片描述

初始化
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,const
pthread_rwlockattr_t *restrict attr); 第二个参数默认为空
销毁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
加锁和解锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

先看基本的 接口使用

#include <iostream>
#include <unistd.h>

#include <pthread.h>

pthread_rwlock_t _rwlock;

static int shared_num = 0;

void* Reader(void* argc)
{
    int id = *(int*)argc;
    while(1)
    {
        //读者申请锁
        pthread_rwlock_rdlock(&_rwlock);
        std::cout << "读者-" << id << "正在读资源: " << shared_num  << std::endl;
        //read

        pthread_rwlock_unlock(&_rwlock);
        sleep(1);
    }

    delete (int*)argc;
    return nullptr;
}

void* Writer(void* argc)
{
    int id = *(int*)argc;

    while(1)
    {
        pthread_rwlock_wrlock(&_rwlock);
        ++shared_num;
        std::cout << "写者-" << id << "正在写资源: " << shared_num  << std::endl;
        sleep(1); //write

        pthread_rwlock_unlock(&_rwlock);
    }
    delete (int*)argc;
    return nullptr;
}


//int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
/*
pref 共有 3 种选择
PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况

PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和PTHREAD_RWLOCK_PREFER_READER_NP 一致

PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁
*/

int main()
{
    pthread_rwlock_init(&_rwlock,nullptr);

    const int readers = 2;
    const int writers = 2;
    const int total = readers +writers;
    pthread_t threads[total]; //管理线程id
    //创建读者和写者线程
    for(int i = 0;i < readers;++i)
    {
        int *ip = new int(i);
        pthread_create(&threads[i],NULL,Reader,ip);
    }
    for(int i = readers;i < total;++i)
    {
        int *id = new int(i - readers);
        pthread_create(&threads[i],NULL,Writer,id);
    }

    //等待线程
    for(int i = 0;i < total;i++)
    {
        pthread_join(threads[i],NULL);
    }
    //线程分离
    // for(int i = 0;i < total;++i)
    // {
    //     pthread_detach(threads[i]); 
    // }

    pthread_rwlock_destroy(&_rwlock);
    return 0;
}

在设置优先级时,尝试过,但是失败了,出现段错误,不知道怎么回事。

  • 解析上面的代码:
  • 默认是读者优先,所以,读者在申请读写锁时,写者会阻塞住,直到所有读者离开临界区,写者才能进入。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邓富民

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值