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

操作系统——读者写者问题

读者写者问题

有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。

因此要求:

①允许多个读者可以同时对文件执行读操作;

②只允许一个写者往文件中写信息;

③任一写者在完成写操作之前不允许其他读者或写者工作;

④写者执行写操作前,应让已有的读者和写者全部退出。

  1. 关系分析。找出题目中各个进程之间的同步、互斥关系。

    同步关系:个人感觉这个问题上并没有很强的同步关系。并不是说读完了,就一定要通知写进程写入数据。写完了,就一定要通知读进程读取数据。

    互斥关系:读进程和写进程不能同时访问文件。写进程和写进程之间也存在互斥关系。但是读进程和读进程不存在互斥关系。

  2. 整理思路并设置信号量。设置需要的信号量,并根据题目中条件确定信号量初值。(互斥信号量初值一般为1)

    文件在这里可以作为一个互斥资源(file,初始值为1),需要注意的是读进程和读进程之间并不存在互斥关系,所以我们需要想办法避免读进程和读进程之间去重复对文件加锁(这里加锁就指的是对信号量进行P操作)的过程。

    写进程在操作之前先尝试能不能加锁成功,不能则阻塞;能则进行操作,操作完成后将资源进行释放。

    读进程就稍稍麻烦一些,大概分为以下步骤:

    • 首先需要维护一个当前正在读取文件的进程计数器。
    • 在读进程读取文件之前先判断计数器是不是为0。
      • 如果为0的话,就证明当前没有任何读进程在读取文件。所以此时需要尝试获取锁,如果不能获取到则证明有写进程在写文件,那么当前进程只能阻塞。如果能获取到锁,就可以读取文件,在真正I/O操作之前需要将计数器加1。
      • 如果不是为0的话,就证明当前还有读进程在读取文件,此时读进程一定是占有锁的,那么当前进程就不用再去获取锁了,可以直接读取文件,同样在读文件之前需要将计数器加1。
    • 在读取文件之后需要判断计数器是不是为1。
      • 如果为不为1的话,则证明还有进程在读取文件,此时锁肯定是不能释放的。计数器需要减1。
      • 如果为1,则证明当前没有进程在读取文件,此时需要释放锁,然后将计数器减1.

按照以上思路,可以简单的先实现一下代码,至于有什么其他问题,实现后再进行分析。

信号量

semapore file = 1;
int i = 0;//读者数量

写进程

void writer(){P(file);写入数据到文件;V(file);
}

读进程

void reader(){if(i == 0){P(file);}i++;从文件中读取数据;i--;if(i == 0){V(file);}}
}

仔细分析一下,读进程的代码存在并发问题,就是在if(i == 0)的地方,如果有n个进程同时进入此判断,那么,必定会有n-1个进程会进入阻塞状态。所以各个读进程对计数器的判断也是属于互斥关系,那么就需要设置一个互斥信号量解决这个互斥问题。

信号量

semapore file = 1; // 解决文件互斥问题的信号量
int i = 0;//读者数量
semapore mutex = 1; // 解决读者数量互斥问题的信号量

写进程

void writer(){P(file);写入数据到文件;V(file);
}

读进程

void reader(){P(mutex);if(i == 0){P(file);}i++;V(mutex);从文件中读取数据;P(mutex);i--;if(i == 0){V(file);}V(mutex);}
}

这样一看,确实是解决了读进程的并发问题,但是如果一直有读进程在读数据的话,写进程就会一直被阻塞,直到饿死。所以还得再想一个方案。

仔细想想,出现读进程一直占用资源的问题无非就是,读写进程对信号量file的上锁的整个过程并不是互斥的,所以再增加一个锁对这个过程进行上锁就可以了。

信号量

semapore file = 1; // 解决文件互斥问题的信号量
int i = 0;//读者数量
semapore mutex = 1; // 解决读者数量互斥问题的信号量
semapore fair = 1;

写进程

void writer(){P(fair);P(file);V(fair);写入数据到文件;V(file);
}

读进程

void reader(){P(fair);P(mutex);if(i == 0){P(file);}i++;V(mutex);V(fair);从文件中读取数据;P(mutex);i--;if(i == 0){V(file);}V(mutex);}
}

http://www.xdnf.cn/news/16968.html

相关文章:

  • Spring **${}** vs **#{}** 语法全景图
  • 【C++ 初级工程师面试--5】inline内联函数特点 、和普通函数的区别、什么时候适合内联?
  • Shell脚本-变量如何定义
  • 什么是DOM和BOM?
  • 搜索引擎评估革命:用户行为模型如何颠覆传统指标?
  • 数据结构1-概要、单向链表
  • [网安工具] Web 漏洞扫描工具 —— AWVS · 使用手册
  • 【C语言】内存函数与数据在内存中的存储
  • python -m build打包成为tar.gz或者whl
  • Qemu-NUC980(二):时钟clock代码添加
  • Redis数据库存储键值对的底层原理
  • SpringBoot相关注解
  • #Linux内存管理#缺页中断处理的核心函数是do_page_fault()的工作原理
  • Vulnhub ELECTRICAL靶机复现(附提权)
  • RPG增容2.尝试使用MMC根据游戏难度自定义更改怪物属性(三)
  • (LeetCode 面试经典 150 题) 138. 随机链表的复制 (哈希表)
  • Kotlin单例模式懒汉模式:LazyThreadSafetyMode.SYNCHRONIZED(2)
  • 深度学习(鱼书)day09--与学习相关的技巧(前三节)
  • P10816 [EC Final 2020] Namomo Subsequence|普及+
  • 机器学习实战:KNN算法全解析 - 从原理到创新应用
  • 【LeetCode 热题 100】(三)滑动窗口
  • Windows下定位Mingw编译的Qt程序崩溃堆栈
  • Python编程基础与实践:Python模块与包入门实践
  • 滚珠花键在汽车制造中有哪些高要求?
  • 什么叫湖仓一体
  • 存储过程的介绍、基本语法、delimiter的使用
  • Effective C++ 条款18:让接口容易被正确使用,不易被误用
  • Qwen3 Embedding:新一代文本表征与排序模型
  • [硬件电路-123]:模拟电路 - 信号处理电路 - 常见的高速运放芯片、典型电路、电路实施注意事项
  • 高效游戏状态管理:使用双模式位运算与数学运算