互斥和同步-读者/写者问题

读者/写者问题

问题定义:

存在一个多进程共享的数据区,该数据区可以是 一个文件或者一块内存或者一组寄存器, 有些进程reader只读取该数据区中的数据, 有些进程writer只往数据区写数据

  • 满足条件

    1. 任意数量的读进程可以同时读该文件
    2. 一次只有一个写进程可以写文件
    3. 若一个写进程正在写文件,则禁止任何进程读文件
  • 综上即: 读进程不需要排斥其他进程,而写进程需要排斥其他所有进程

一般互斥解决方案

  1. 因为读进程写进程 都要访问 数据区,那么将 进程访问 数据区的部分声明为一个临界区
  • 缺点:

    • 假设共享区是一个图书馆目录,普通用户访问目录查找书,管理员可以修改目录,
    1. 一般情况下,每次访问目录都相当于访问临界区,且用户每次又只能访问一个目录, 导致延迟过高,

读者优先 的解决方案

  • 思路:
  1. 信号量 wsem 用于实施互斥(writesemaphore), 当写进程正在访问共享数据区时,其他进程不能访问,

  2. 同时读进程也通过wsem实施互斥,

    • 读者优先:

      1. 没有一个读进程在执行时, 第一个试图读的读进程在wsem上等待

      2. 已经存在至少一个读进程时, 随后的读进程无需等待,可直接进入

  3. readcount用于记录读进程的数量

  4. 信号量x用于确保readcount被正确更新

/*program readers and writers*/
int readcount;
semaphore x = 1,wsem = 1;
void reader() {
    while (true) {
        semWait(x); //信号量x 保证readcount 操作安全
       	readcount++;
        if (readcount == 1)
            semWait(wsem); // 第一个试图读的进程需要等待 wsem (写进程可能在执行)
        semSignal(x);   //更行x
        
        READUNIT();   //读操作
        
        semWait(x); //再次修改readcount
        readcount--;
        if (readcount == 0)
            semSignal(wsem); // 此时没有读进程了, 增加信号量wsem 
        semSignal(x);
    }
}
void writer() { //写进程互斥只由 信号量wsem决定
    while (true) {
        semWait(wsem);
        WRITEUNIT();
        semSignal(wsem);
    }
}
void main() {
    readcount = 0; //更新读进程的个数
    parbegin(reader,writer);
}

写者优先 的解决方案

  • 读者优先的问题:

    当一个读进程开始访问数据区时,只要至少有一个读进程正在读,那么为该进入的读进程 保留 数据区的控制权, 导致写进程可能处于饥饿状态

  • 目的是解决 写进程可能处于饥饿的状态

  • 思路

    • 信号量rsem: 至少有一个写进程准备访问数据区时,用于禁止所有的读进程.
      * 0 : 有写进程,即 写进程准备访问数据区
      * 1 : 无准备访问数据区的 写进程,
    • 变量 writecount: 控制rsem的设置
    • 信号量y:类似x: 控制writecount的修改
/*program readers and writers*/
int readcount,writecount;
semaphore x=1,y=1,z=1,wsem=1,rsem=1;
void reader() {
    while(true) {
        semWait(z);
        //rsem信号决定 1. rsem=0 新的读进程不能访问数据区 2. rsem=1 读进程可以访问数据区
        semWait(rsem); //由rsem信号决定 新的读进程 能不能访问数据区
        semWait(x);

        readcount++;
        if (readcount == 1)
            semWait(wsem);

        semSignal(x);
        semSignal(rsem);
        semSignal(z);

        READUNIT();

        semWait(x);

        readcount--;
        if (readcount == 0)
            semSignal(wsem);

        semSignal(x);
    }
}
void writer() {
    while(true) {
        semWait(y);
        
        writecount++;
        if (writecount == 1)
            semWait(rsem); //检测rsem情况 告诉读进程 写进程即将访问数据区
        // rsem=0 等待读进程执行完后 写进程开始访问数据区 rsem=1 此时数据区无进程
      
        semSignal(y);
        semWait(wsem); // wsem信号量控制读/写进程间的互斥
        WRITEUNIT();
        semSignal(wsem);
        semWait(y);
        writecount--;
        if (writecount == 0)
            semSignal(rsem); //
        semSignal(y);
    }
}
void main() {
    readcount = writecount = 0;
    parbegin(reader,writer);
}
  • rsem的作用(个人理解)
    1. rsem是为写进程而定义的, 在wsem提供 读/写进程互斥的基础上 .又通过rsem信号量提供: 当一个写进程准备进入数据区时, 保证新的读进程阻塞(不能进入数据区) 且写进程 等待已有的读进程退出数据区
信号量z的作用
  • 由于可能有多个读进程都在等待rsem信号量,通过信号量z使得等待rsem信号量的队列改成在信号量z上排队,那么每次只有一个只读进程在rsem上排队

消息传递实现写进程的优先权 的解决方案

  • 流程:

    • 一个访问共享数据区的控制进程,其他访问该数据区的进程给控制进程发送消息请求,
    1. 若请求同意,会接收到控制进程发送的应答消息OK,并通过finished消息表示访问完成.
    2. 控制进程有3个信箱,接收对应的消息,finished,writerequest,readrequest
    void reader(int i) {
    	message rmsg;
    	while (true) {
    		rmsg = i;
    		send(readrequest,rmsg); //发送地址:readrequest
    		receive(mbox[i],rmsg); //来源地址:mbox[i],
    		READUNIT();
    		rmsg = i;
    		send(finished,rmsg); //发送写完成的消息
    	}
    }
    void writer(int j) {
    	message rmsg;
    	while (true) {
    		rmsg = j;
    		send(writerequest,rmsg); // 第一次请求写的消息
    		receive(mbox[j],rmsg); //接收控制进程的同意消息
    		WRITEUNIT();
    		rmsg = j;
    		send(finished,rmsg); //发送 写操作 完成消息
    	}
    }
    void controller() {
    	while (true) {
            if (count > 0) {
                // 在处理 写 消息之前, 先处理所有的finished请求
                if (!empty(finished)) {
                   	receive();
                    count++;
                }
                else if (!empty(writerequest)) {
    				receive(writerequest,msg);
                    writer id = msg.id;
                    count  = count - 100;
                }
                else if (!empty(readrequest)) {
    				receive(readrequest,msg);
                    count--;
                    send(msg.id,"OK");
                }
            }
            if (count == 0) {
            	send(writer id,"OK");
                receive(finished,msg);
                count = 100;
        	}
        	wihle (count < 0) {
            	receive(finished,msg);
                count++;
        	} 
        }
    }
    
  • 流程分析

    1. count初始化为大于可能的读进程数的最大值.假设为100, 对读请求消息操作数为1,对写请求操作数为-100
    • count > 0说明没有读进程在等待,且可能有读进程发出的finished消息未处理,然后再处理写请求的消息,最后处理读请求的消息
    • count == 0写进程发送给控制进程请求消息后收到OK,并且又发送finished给控制进程表示完成,然后控制进程恢复count = 100
    • count < 0 , 写进程发出一个请求消息,那么要消除所有读进程,提供receive(finished,msg).
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值