操作系统PV问题——一个略为复杂的读者写者问题

操作系统PV问题——一个略为复杂的读者写者问题

昨天在西班牙某技术论坛上看到一个有意思的PV操作题目,和传统的读写操作不一样,他这里多了一个缓冲区。想着之前自己操作系统学的不太扎实,于是就拿着练练手了。

原题目是这么说的:

Tenemos n 1 n_1 n1 procesos A i A_i Ai que realizan operaciones de escritura ( 1 < = i < = n 1 1<=i<=n_1 1<=i<=n1) y n 2 n_2 n2 procesos B i B_i Bi que realizan operaciones de lectura ( 1 < = i < = n 2 1<=i<=n_2 1<=i<=n2), donde los A i A_i Ai están constantemente enviando mensajes a los B i B_i Bi a través de m buffers. La comunicación de mensajes entre ellos sigue las siguientes tres reglas:

  1. Cada proceso emisor A i A_i Ai envía un mensaje a la vez y llena una memoria de búfer;

  2. Cada proceso receptor B i B_i Bi recibe un mensaje de A i A_i Ai sólo una vez;

  3. Si todas las m m m memorias de búfer están ocupadas, el proceso emisor espera; y si todas ellas están vacías, el proceso receptor espera.

Escriba un pseudocódigo para el algoritmo anterior utilizando operaciones P/V e impleméntelo en cualquier lenguaje de programación (C/C++/Java/Python).

翻译成人话就是:有 n 1 n_1 n1个进程 A i A_i Ai通过 m m m个缓冲区向 n 2 n_2 n2个进程 B i B_i Bi发送 m s g msg msg条数据,然后用P/V操作设计一个算法,使得A的写和B的读操作互斥:A写满缓冲区时A挂起,B读空缓冲区时B也得等待。

那么首先来分析题目,题目中出现了一个缓冲区,是临界资源。是个人都知道这玩意必须要用一个信号量保护起来以免冲突,于是就定义一个信号量mutex,刚开始是没有使用的因此初值为1。缓冲区内部又分为n个块,读取和写入每一个块的时候又可能会冲突,所以对于写者得有一个判定是否还有空缓冲区块的信号量empty[0…n_1],并且初始状态下写进程没写入任何数据,缓冲区完全为空,故将empty数组的初值全部置为m。对于每个读者进程,需要有一个表示剩余可读量的信号量readable[i],并置数组初值为0,表示初始状态下写进程没写入数据时没有数据可以从缓冲区读入。

那么将上述操作转换为伪代码就是:

// 写者进程
void Escribir() {
    while(1) {
        // for every write process, be ready to write to buffer
        for(int i = 0; i < n1; i++) P(empty[i]);
        // take possession of buffer
        P(mutex);
        消息放入缓冲区;
        // release buffer
        V(mutex)
        // for every read process, notify that it can read a piece of message
        for(int i = 0; i < n2; i++) V(readable[i]);
    }
}
// 读者进程
void Leer() {
    while(1) {
        for(int i = 0; i < n2; i++) P(readable[i]); // read a piece of message
        P(mutex); // take possession of buffer
        读取缓冲区;
        // release buffer
        V(mutex);
        for(int i = 0; i < n1; i++) V(empty[i]); // now a block in buffer has been freed
    }
}

下面我用Java来实现,并且使用Java的线程类Thread来模拟进程(但并不等同于进程!!这点一定要注意)。假设有 m s g msg msg条消息要传送,上述伪代码中,消息存入缓冲区可以模拟为:设未发送消息量为 r e m rem rem,初值为 m s g msg msg,每执行while循环体内一次该操作,就将 r e m rem rem自减1,最后输出“已向缓冲区写入一条数据”以及 r e m rem rem的值,并且在每次while循环体开头处检查所有消息是否传送完毕,如果传送完毕就退出循环,结束写者线程A。从缓冲区中读取消息的模拟方法和上述写者进程的操作类似。

Java中信号量是用Semaphore类来实现的,P/V操作则分别对应于信号量对象的成员函数acquire()和release()。

最终代码如下:

import java.util.*;
import java.util.concurrent.*;

public class Main {

    private int n1, n2, m, msg;
    private Semaphore mutex = new Semaphore(1);
    private Semaphore[] readable, empty;

    class Escribir implements Runnable {
        @Override
        public void run() {
            int rem = msg;
            try {
                while(true) {
                    if(rem <= 0) break;
                    for(int i = 0; i < n1; i++) empty[i].acquire();
                    mutex.acquire();
                    System.out.println("Wrote 1 byte msg to buffer, now remains: " + (--rem));
                    mutex.release();
                    for(int i = 0; i < n2; i++) readable[i].release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    class Leer implements Runnable {
        @Override
        public void run() {
            int cnt = 0;
            try {
                while(true) {
                    if(cnt >= msg) break;
                    for(int i = 0; i < n2; i++) readable[i].acquire();
                    mutex.acquire();
                    System.out.println("Read 1 byte msg from buffer, until now received: " + (++cnt));
                    mutex.release();
                    for(int i = 0; i < n1; i++) empty[i].release();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public Main() {
        Scanner sc = new Scanner(System.in);
        System.out.println("How many readers:");
        n1 = sc.nextInt();
        System.out.println("How many writers:");
        n2 = sc.nextInt();
        System.out.println("How many blocks in buffer:");
        m = sc.nextInt();
        System.out.println("How many messages?");
        msg = sc.nextInt();
        empty = new Semaphore[n1+1];
        readable = new Semaphore[n2+1];
        Arrays.fill(empty, new Semaphore(m));
        Arrays.fill(readable, new Semaphore(0));
        Thread t1 = new Thread(new Escribir()), t2 = new Thread(new Leer());
        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        new Main();
    }
}

输入 n 1 n_1 n1 = 5, n 2 n_2 n2 = 4, m m m = 6, m s g msg msg = 12 时,运行效果如下:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值