【操作系统-进程】PV操作——读者写者问题

这篇具有很好参考价值的文章主要介绍了【操作系统-进程】PV操作——读者写者问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

读者写者问题万能模板

读者写者问题,其本质就是连续多个同类进程访问同一个临界资源的问题。

第一个进程开始访问临界资源前,需要对资源加上互斥锁,后面的进程再访问时就不用再对资源加互斥锁了,直到最后一个进程访问完后,发现自己是最后一个进程,就解锁互斥锁。这就像一种情况:第一个人进房间时必须顺手开门,后面进来的人和离开的人就不用开门,直到最后一个人离开房间时才需要顺手关门。

代码的通用模板是“三段式”,如下:

int count = 0; // 记录正在访问的进程数量
信号量 busy = 1; // “完成事件”的互斥锁
信号量 mutex = 1; // 变量 count 的互斥锁

Process(){
    while(1){
        P(mutex);
        count++; // 访问资源的进程数量加 1
        if (count == 1){ // (1)如果发现自己是第一个访问的进程,需要负责加锁
            P(busy);
        }
        V(mutex);
        
        完成事件; // (2)访问临界资源、完成临界事件
        
        P(mutex);
        count--; // 访问完毕,访问资源的进程数量减 1
        if (count == 0){ // (3)如果发现自己是最后一个访问的进程,需要负责解锁
            V(busy);
        }
        V(mutex);
    }
}

理解了最基本的原理后,下面来正式讨论读者写者问题。

【读者写者问题】有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程(只是读数据,不会对数据产生影响,而消费者读数据时,会将数据取走,因此不能两个消费者一起读数据)同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。

因此要求:

  • 允许多个读者可以同时对文件执行读操作;
  • 只允许一个写者往文件中写信息;
  • 任一写者在完成写操作之前不允许其他读者或写者工作;
  • 写者执行写操作前,应让已有的读者和写者全部退出。

万能模板 1——读进程优先

即使写者发出了请求写的信号,但是只要还有读者在读取内容,就还允许其他读者继续读取内容,直到所有读者结束读取,才真正开始写。

int count = 0;
信号量 busy = 1; // “读文件”和“写文件”的互斥锁
信号量 mutex = 1; // 变量 count 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(mutex);
        count++; 
        if (count == 1){ 
            P(busy);
        }
        V(mutex);
        
        读文件; 
        
        P(mutex);
        count--; 
        if (count == 0){ 
            V(busy);
        }
        V(mutex);
    }
}

Writer(){ // 写者进程
    while(1){
        P(busy);
        写文件;
        V(busy);
    }
}

如果读者写者到达的顺序是:读者 1–读者2–读者 3–写者 A–读者 4–写者 B–读者 5,则:

  • 读者 1、读者 2、读者 3 到达,busy 加锁,开始读;
  • 写者 A 到达,但是读者还在读,未释放 busy 锁,因此不能写;
  • 读者 4 到达,可以开始读;
  • 写者 B 到达,但是读者还在读,未释放 busy 锁,因此不能写;
  • 读者 5 到达,可以开始读;
  • 等到读者们都读完,释放 busy 锁,写者才可以开始写。

万能模板 2——读写公平法

读写进程都要排队进行操作文件。即使里面有读进程在操作文件,读进程也要和写进程一起排队。

int count = 0;
信号量 queue = 1; // 实现“读写公平”的互斥锁,可以视为一个队列
信号量 busy = 1; // “读文件”和“写文件”的互斥锁
信号量 mutex = 1; // 变量 count 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(queue); // 在无写进程请求时不需要进入队列
        P(mutex); // 该互斥量实际上是多余的,上面语句已经兼有互斥功能
        count++; 
        if (count == 1){ 
            P(busy);
        }
        V(mutex); // 该互斥量实际上是多余的,下面语句已经兼有互斥功能
        V(queue); // 恢复对共享文件的访问
        
        读文件; 
        
        P(mutex);
        count--; 
        if (count == 0){ 
            V(busy);
        }
        V(mutex);
    }
}

Writer(){ // 写者进程
    while(1){
        P(queue); // 在无其他写进程请求时不需要进入队列
        P(busy);
        写文件;
        V(busy);
        V(queue); // 恢复对共享文件的访问
    }
}

如果读者写者到达的顺序是:读者 1–读者2–读者 3–写者 A–读者 4–写者 B–读者 5,则:

  • 读者 1、读者 2、读者 3 到达,busy 加锁,开始读;
  • 写者 A 到达,queue 加锁,但是读者还在读,busy 锁仍未释放,因此不能写;
  • 读者 4 到达,但是读者还在读,queue 和 busy 锁仍未释放,不能开始读;
  • 写者 B 到达,queue 锁和 busy 锁仍未释放,因此不能写;
  • 读者 5 到达,但是读者还在读,queue 和 busy 锁仍未释放,不能开始读;
  • 等到读者 1 到 3 都读完,释放 busy 锁,写者 A 才可以开始写;
  • 接下来就是按读者 4、写者 B、读者 5 的顺序依次访问文件。

实际上,读写公平法也可以不用任何除了访问文件外的互斥锁:

信号量 busy = 1; // 也可以视为一个队列

Reader(){ // 读者进程
    while(1){
        P(busy);
        读文件;
        V(busy);
    }
}

Writer(){ // 写者进程
    while(1){
        P(busy);
        写文件;
        V(busy);
    }
}

万能模板 3——写进程优先

如果有写者申请写文件,那么在申请之前还在读取文件的读进程可以继续读取,但是如果再有读者申请读取文件,则不能够读取,只有在所有的写者写完之后才可以读取。

int ReaderCount = 0; // 读者数量
int WriterCount = 0; // 写者数量
信号量 Read = 1; // “读文件”的互斥锁
信号量 Write = 1; // “写文件”的互斥锁
信号量 ReaderMutex = 1; // 变量 ReaderCount 的互斥锁
信号量 WriterMutex = 1; // 变量 WriterCount 的互斥锁

Reader(){ // 读者进程
    while(1){
        P(Read); // 每个读进程都需要对 Read 加锁
        P(ReaderMutex); // 对 ReadCount 的互斥,实际上,上条语句已经兼有此功能,可以去掉
        ReaderCount++; 
        if (ReaderCount == 1){ // 如果是第一个读进程
            P(Write); // 则对写者上锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥,实际上,下条语句已经兼有此功能,可以去掉
        V(Read); // Read 解锁
        
        读文件; 
        
        P(ReaderMutex); // 对 ReadCount 的互斥
        ReaderCount--; 
        if (ReaderCount == 0){ // 如果是最后一个读进程
            V(Write); // 则对写者解锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥
    }
}

Writer(){ // 写者进程
    while(1){
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount++; 
        if (WriterCount == 1){ // 如果是第一个写进程
            P(Read); // 则对读者上锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
        
        P(Write); // Write 加锁
        写文件; 
        V(Write); // Write 解锁
        
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount--; 
        if (WriterCount == 0){ // 如果是最后一个写进程
            V(Read); // 则对读者解锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
    }
}

如果读者写者到达的顺序是:读者 1–读者2–读者 3–写者 A–读者 4–写者 B–读者 5,则:

  • 读者 1、读者 2、读者 3 到达,Write 加锁,开始读;
  • 写者 A 到达,Read 加锁,但是读者还在读,Write 锁仍未释放,因此不能写;
  • 读者 4 到达,Read 锁仍未释放,不能开始读;
  • 写者 B 到达,Write 锁仍未释放,因此不能写;
  • 读者 5 到达,Read 锁仍未释放,不能开始读;
  • 等到读者 1 到 3 都读完,释放 Write 锁,写者 A 才可以开始写,写完后 Write 解锁。由于写者 A 不是最后一个写进程,因此 Read 不解锁;
  • 写者 B 进行写操作,写完后 Write 解锁,由于写者 B 是最后一个写进程,因此 Read 解锁;
  • 读者 4、读者 5 依次进行读操作。

题目 1:南北过桥问题

【题目 1】有桥如下图所示,车流如箭头所示,桥上不允许两车交汇,但允许同方向多辆车依次通过(即桥上可以有多个同方向的车)。用P、V操作实现交通管理以防止桥上堵塞。

读者写者问题,# 计算机操作系统,408,同步,互斥,算法

【解答】直接套“三段式”,如下:

int count1 = 0; // 北到南的车辆数
int count2 = 0; // 南到北车辆数
信号量 bridge = 1;
信号量 mutex1 = 1;
信号量 mutex2 = 1;

北到南(){
    P(mutex1);
    count1++;
    if (count1 == 1){
        P(bridge);
    }
    V(mutex1);
    
    北到南过桥;
    
    P(mutex1);
    count1--;
    if (count1 == 0){
        V(bridge);
    }
    V(mutex1);   
}

南到北(){
    P(mutex2);
    count2++;
    if (count2 == 1){
        P(bridge);
    }
    V(mutex2);
    
    南到北过桥;
    
    P(mutex2);
    count2--;
    if (count2 == 0){
        V(bridge);
    }
    V(mutex2);   
}

题目 2:录像厅问题

【题目 2】假设一个录像厅有 0,1,2 三种不同的录像片可由观众选择放映。录像厅的放映规则为:

  • 任何时刻最多只能放映一种录像片,正在放映的录像片是自动循环放映的。最后一个观众主动离开时结束当前录像片的放映。
  • 选择当前正在放映录像片的观众可立即进入,允许同时有多位选择同一中录像片的观众同时观看,同时观看的观众数量不受限制。
  • 等待观看其他录像片的观众按到达顺序排队,当一种新的录像片开始放映时,所有等待观看该录像片的观众可一次进入录像厅同时观看。

【解答 1】本题也可以直接套“三段式”,如下:

int count0 = 0; // 看影片 0 的观众数
int count1 = 0; // 看影片 1 的观众数
int count2 = 0; // 看影片 2 的观众数
信号量 movie = 1;
信号量 mutex0 = 1;
信号量 mutex1 = 1;
信号量 mutex2 = 1;

看影片0的观众(){
    P(mutex0);
    count0++;
    if (count0 == 1){
        P(movie);
    }
    V(mutex0);
    
    看影片0;
    
    P(mutex0);
    count0--;
    if (count0 == 0){
        V(movie);
    }
    V(mutex0);
}

看影片1的观众(){
    P(mutex1);
    count1++;
    if (count1 == 1){
        P(movie);
    }
    V(mutex1);
    
    看影片1;
    
    P(mutex1);
    count1--;
    if (count1 == 0){
        V(movie);
    }
    V(mutex1);
}

看影片2的观众(){
    P(mutex2);
    count2++;
    if (count2 == 1){
        P(movie);
    }
    V(mutex2);
    
    看影片2;
    
    P(mutex2);
    count2--;
    if (count2 == 0){
        V(movie);
    }
    V(mutex2);
}

【解答 2】借助“写进程优先”的思想,观看某影片的观众在进去前,可以先把要观看其他影片的观众先上锁,让他们暂时阻塞,如下:

int count0 = 0; // 看影片 0 的观众数
int count1 = 0; // 看影片 1 的观众数
int count2 = 0; // 看影片 2 的观众数
信号量 mutex0 = 1; // 影片 0 的互斥锁,兼有对变量 count0 的互斥功能
信号量 mutex1 = 1; // 影片 1 的互斥锁,兼有对变量 count1 的互斥功能
信号量 mutex2 = 1; // 影片 2 的互斥锁,兼有对变量 count2 的互斥功能


看影片0的观众(){
    P(mutex0);
    count0++;
    if (count0 == 1){ // 第一个进去的观众把其他影片的观众“挡住”
        P(mutex1);
        P(mutex2);
    }
    V(mutex0);
    
    看影片0;
    
    P(mutex0);
    count0--;
    if (count0 == 0){ // 最后一个出来的观众允许其他影片的观众进来
        V(mutex1);
        V(mutex2);
    }
    V(mutex0);
}


看影片1的观众(){
    P(mutex1);
    count1++;
    if (count1 == 1){ // 第一个进去的观众把其他影片的观众“挡住”
        P(mutex0);
        P(mutex2);
    }
    V(mutex1);
    
    看影片1;
    
    P(mutex1);
    count1--;
    if (count1 == 0){ // 最后一个出来的观众允许其他影片的观众进来
        V(mutex0);
        V(mutex2);
    }
    V(mutex1);
}


看影片2的观众(){
    P(mutex2);
    count2++;
    if (count2 == 1){ // 第一个进去的观众把其他影片的观众“挡住”
        P(mutex0);
        P(mutex1);
    }
    V(mutex2);
    
    看影片2;
    
    P(mutex2);
    count2--;
    if (count2 == 0){ // 最后一个出来的观众允许其他影片的观众进来
        V(mutex0);
        V(mutex1);
    }
    V(mutex2);
}

看似没毛病吧。然而,事实上这段代码是有问题的,可能会引发死锁,哈哈哈……你发现了吗?

题目 3:更衣问题

【题目 3】某男⼦⾜球俱乐部,有教练、队员若⼲。每次⾜球训练开始之前,教练、球员都需要先进⼊更⾐室换⾐服,可惜俱乐部只有⼀个更⾐室。教练们脸⽪薄,⽆法接受和别⼈共⽤更⾐室。队员们脸⽪厚,可以和其他队员⼀起使⽤更⾐室。如果队员和教练都要使⽤更⾐室,则应该让教练优先。请使⽤ P、V 操作描述上述过程的互斥与同步,并说明所⽤信号量及初值的含义。

【解答】把教练视为写进程,把队员视为读进程,更衣室视为缓冲区,则该题需要实现的“写进程优先”。如下:文章来源地址https://www.toymoban.com/news/detail-794771.html

int ReaderCount = 0; // 读者数量
int WriterCount = 0; // 写者数量
信号量 Read = 1; // “读文件”的互斥锁
信号量 Write = 1; // “写文件”的互斥锁
信号量 ReaderMutex = 1; // 变量 ReaderCount 的互斥锁
信号量 WriterMutex = 1; // 变量 WriterCount 的互斥锁

Reader(){ // 读者进程(相当于教练)
    while(1){
        P(Read); // 每个读进程都需要对 Read 加锁
        P(ReaderMutex); // 对 ReadCount 的互斥
        ReaderCount++; 
        if (ReaderCount == 1){ // 如果是第一个读进程
            P(Write); // 则对写者上锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥
        V(Read); // Read 解锁
        
        读文件; 
        
        P(ReaderMutex); // 对 ReadCount 的互斥
        ReaderCount--; 
        if (ReaderCount == 0){ // 如果是最后一个读进程
            V(Write); // 则对写者解锁
        }
        V(ReaderMutex); // 对 ReadCount 的互斥
    }
}

Writer(){ // 写者进程(相当于队员)
    while(1){
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount++; 
        if (WriterCount == 1){ // 如果是第一个写进程
            P(Read); // 则对读者上锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
        
        P(Write); // Write 加锁
        写文件; 
        V(Write); // Write 解锁
        
        P(WriterMutex); // 对 WriterCount 的互斥
        WriterCount--; 
        if (WriterCount == 0){ // 如果是最后一个写进程
            V(Read); // 则对读者解锁
        }
        V(WriterMutex); // 对 WriterCount 的互斥
    }
}

到了这里,关于【操作系统-进程】PV操作——读者写者问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机操作系统实验:进程调度实验

    前言 二、实验目的 三、实验要求 四、实验原理 五、实验过程 六、代码详解 总结 计算机操作系统是管理计算机硬件和软件资源的核心软件,它负责为用户提供一个友好、高效、安全的使用环境。进程调度是操作系统的一个重要功能,它决定了进程在处理器上的执行顺序和时

    2024年02月07日
    浏览(39)
  • 计算机操作系统实验-进程调度模拟算法

    进程调度是处理机管理的核心内容。本实验要求用高级语言编写模拟进程调度程序,以 便加深理解有关进程控制快、进程队列等概念,并体会和了解优先数算法和时间片轮转算法 的具体实施办法。 1.设计进程控制块 PCB 的结构,通常应包括如下信息: 进程名、进程优先数(

    2024年02月05日
    浏览(44)
  • 【lesson59】线程池问题解答和读者写者问题

    单例模式是一种 “经典的, 常用的, 常考的” 设计模式. IT行业这么火, 涌入的人很多. 俗话说林子大了啥鸟都有. 大佬和菜鸡们两极分化的越来越严重. 为了让我们这些菜鸡们不太拖大佬的后腿, 于是大佬们 针对一些经典的常见的场景, 给定了一些对应的解决方案 , 这个就是设

    2024年02月21日
    浏览(28)
  • 计算机操作系统重点概念整理-第三章 进程同步【期末复习|考研复习】

    计算机操作系统复习系列文章传送门: 第一章 计算机系统概述 第二章 进程管理 第三章 进程同步 第四章 内存管理 第五章 文件管理 第六章 输出输出I/O管理 给大家整理了一下计算机操作系统中的重点概念,以供大家期末复习和考研复习的时候使用。 参考资料是王道的计算

    2024年02月08日
    浏览(37)
  • 计算机操作系统重点概念整理-第二章 进程管理【期末复习|考研复习】

    计算机操作系统复习系列文章传送门: 第一章 计算机系统概述 第二章 进程管理 第三章 进程同步 第四章 内存管理 第五章 文件管理 第六章 输出输出I/O管理 给大家整理了一下计算机操作系统中的重点概念,以供大家期末复习和考研复习的时候使用。 参考资料是王道的计算

    2024年02月08日
    浏览(41)
  • 计算机操作系统【慕课版】习题答案(第2章进程的描述与控制)

    一:简答题 (1).什么是前趋图?试画出下面四条语句的前趋图. S1:a=x+y; S2:b=z+1; S3:c=a-b; S4:w=c+1; 答:前趋图(Precedence Graph)是一个有向无循环图,记为DAG(DirectedAcyclicGraph),用于描述进程之间执行的前后关系。 (2)什么是进程? OS中为什么要引入进程?它会产生什么样的

    2024年04月13日
    浏览(27)
  • 用信号量机制解决读者-写者问题C语言实现

    文章目录 介绍 一、什么是进程同步,进程互斥 二、读者-写者问题概述 1.概念图 2.实例代码 总结 通过实验模拟读者和写者之间的关系,了解并掌握他们之间的关系及其原理。由此增加对进程同步的问题的了解。具体如下:   1)掌握基本的同步互斥算法,理解读者和写者模型

    2024年02月02日
    浏览(28)
  • 实现时间片轮转算法(模拟)计算机操作系统实验5:进程调度算法模拟-RR

    实验内容: 实现时间片轮转算法(模拟),要求如下: 1、用到的数据结构 /* PCB / struct PCB { pid_t pid;//进程 PID int state; //状态信息,1 表示正在运行,0 表示暂停,-1 表示结束 unsigned long runned_time;//已运行时间 unsigned long need_running_time;//剩余运行时间 }; / PCB集合 */ struct PCB pcb[TOT

    2024年02月04日
    浏览(44)
  • linux线程池、基于线程池的单例模式、读者写者问题

    线程池: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。

    2024年02月03日
    浏览(29)
  • 【Linux】线程池设计/单例模式/STL、智能指针与线程安全/读者写者问题

    线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可

    2024年02月03日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包