当前位置: 首页 > news >正文

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

文章目录

  • 一、读者写者问题
    • 读者写者问题vs生产消费模型
    • 理解读者写者模型的原理
    • 读者优先和写者优先
  • 从代码角度理解读者写者模型
    • 读写锁使用的基本接口


一、读者写者问题

之前学过的生产消费者模型中,有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;
}

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

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

相关文章:

  • C/C++ 调用约定:深入理解栈与平栈
  • OpenVLA-OFT——微调VLA时加快推理的三大关键设计:支持动作分块的并行解码、连续动作表示以及L1回归(含输入灵活化及对指令遵循的加强)
  • 第16届蓝桥杯单片机模拟试题Ⅱ
  • Java 大视界 -- Java 大数据机器学习模型在智能客服多轮对话系统中的优化策略(179)
  • 计算机系统---性能指标(3)续航与散热
  • 【C++篇】深入剖析C++ Vector底层源码及实现机制
  • 数据分享:汽车测评数据
  • ARM 汇编启动代码详解:从中断向量表到中断处理
  • 浪漫永恒怀旧婚礼结婚照户外摄影照片调色Lightroom预设 J SERIES PRESETS
  • Java反射实战-特殊嵌套格式JSON自定义解析装配
  • Exce格式化批处理工具详解:高效处理,让数据更干净!
  • C语言-查表法详解与实践
  • 网络游戏服务器如何构建全方位防御体系?DDoS与CC攻击实战防护指南
  • 鸿蒙开发_ARKTS快速入门_语法说明_自定义组件开发---纯血鸿蒙HarmonyOS5.0工作笔记011
  • 如何将/dev/ubuntu-vg/lv-data的空间扩展到/dev/ubuntu-vg/ubuntu-lv的空间上
  • 杂谈:模型训练参数是否存在临界点?
  • DNS服务(Linux)
  • 软考笔记9——数据库技术基础
  • python应用之使用pdfplumber 解析pdf文件内容
  • 第二篇:系统分析师——7-11章
  • 雄安做网站要多少钱/seo公司外包
  • 新闻类的网站有哪些类型/国内搜索引擎有哪些
  • 进口彩妆做的好的网站/网络管理系统
  • 网站单页别人是怎么做的/黑帽seo技巧
  • 长沙房产网最新楼盘/seo外链购买
  • 东莞疫情政策/seo优化上首页