【汤4操作系统】深入理解信号量的使用-三大问题的变体

本文介绍了使用信号量解决经典并发问题的方法,包括生产者消费者问题的各种变体,如理发师问题、单缓冲区和多缓冲区生产者消费者问题,以及读写者问题和哲学家问题的变体。通过信号量实现进程间的同步和互斥,确保系统资源的有效利用和程序的正确执行。
摘要由CSDN通过智能技术生成

主要从生产者消费者、读写者、哲学家问题中的经典变体进行讲述,均使用伪代码实现

生产者消费者变体

在这里插入图片描述

顾客看作是生产出的产品,理发师看作是消费者,沙发有空位,顾客就进去,沙发有顾客,理发师就去理发

和生产者消费者的区别在于

  • 生产者生产出一个产品后,当缓存没有空位时就会一直等待下去,直到有空位的出现
  • 此问题如果没有空位,用户直接离开,不会加入阻塞队列
semaphore customers = 0,barber = 0;//沙发上的顾客数和正在理发的理发师数目
semaphore mutex = 1;
int count = 0;//沙发上的顾客人数,用来技术
void barber() {
    do {
		wait(customer);//如果沙发有顾客,就起来理发,否则阻塞理发师睡觉
        wait(mutex);//保护count
        count = count - 1;//等待的顾客数减一,开始剪头
        signal(babers); //通知理发师可以开始理发
        signal(mutex);
        haircut();
    }while(TRUE);
}
//没有空位顾客直接离开,所以不需要while循环
void customer() {
    wait(mutex);
    if (count < n) {
        //如果有空椅子则等待的顾客数+1
        count = count + 1;
        signal(customers);
        signal(mutex);
        wait(barbers);//如果理发师忙则等待
    }else {
		signal(mutex);//无椅子离开
    }
}

单缓冲区生产者消费者

有一计算进程和打印进程,它们共享一个单缓冲区,计算进程不断的计算出一个整形结果并将它放入缓冲区,打印进程则负责从单缓冲区中取出每一个结果进行打印

在这里插入图片描述

生产者消费者-单缓冲区

//生产者消费者的完整复杂写法
semaphore mutex = 1;
semaphore full = 0,empty = 1;
int buffer[1];
int in = 0,out = 0;
void calculator() {
    int nextp;
    do {
        calculate into nextp;
       	wait(empty);
        wait(mutex);
        buffer[in] = nextp;
        in = (in + 1) % 2;
        signal(mutex);
        signal(full);
    }while(TRUE);
}

void printer() {
    int nextc;
	do {
        wait(full);
        wait(mutex);
        nextc = buffer[out];
        out = (out + 1) % 2;
        signal(mutex);
        signal(full);
      	print(nextc);
    }while(TRUE);
}

对于单缓冲区而言,可以不需要mutex、buffer数组和in out指针

//生产者消费者的完整复杂写法
semaphore full = 0,empty = 1;
int buffer;
void calculator() {
    int nextp;
    do {
        calculate into nextp;
       	wait(empty);
        buffer = nextp;
        signal(full);
    }while(TRUE);
}

void printer() {
    int nextc;
	do {
        wait(full);
        nextc = buffer;
        signal(full);
      	print(nextc);
    }while(TRUE);
}


多缓冲区生产者消费者变体

有三个进程PA、PB、PC协作解决文件打印问题,PA将文件记录从磁盘读入内存的缓冲区1,每执行一次读入一个记录;PB将缓冲区1的内容复制到缓冲区2中,每执行一次复制一条记录;PC将缓冲区2的内容打印出来,每执行一次打印一条;缓冲区的大小和记录大小一样,请用信号量来保证文件的正确打印。

分析:

该问题中PA是缓冲区1的生产者,PB是缓冲区1的消费者、缓冲区2的生产者,PC是缓冲区2的消费者

该问题为多缓冲区多生产者消费者的变体

因为缓冲区大小为1,故不需要互斥访问量

semaphore full1 = 0,full2 = 0;
semaphore empty1 = 1,empty2 = 1;
item buff1;
item buff2;
void PA() {
    item read1;
    do {
		//从磁盘读入read1;
        wait(empty1);
        buff1 = read1;
        signal(full1);
    }while(true);
}
void PB() {
    item tmp;
    do {
        wait(full1);
        tmp = buff1;//取出缓冲区1的数据
        signal(empty1);
        wait(empty2);
        buff2 = tmp;//存入缓冲区2
        signal(full2);
    }while(true);
}
void PC() {
    item print;
    do {
        wait(full2);
        print = buff2;//取出数据
        signal(empty2);
        print(print);//打印
    }
}

生产者消费者变形

进程A1、A2、…An1通过m个缓冲区向进程B1、B2…Bn2不断地发送消息。发送和接收工作遵循如下规则:

  1. 每个发送进程一次发送一个消息,写入一个缓冲区,缓冲区大小与消息长度相同
  2. 对每个消息,B1、…Bn2都需各自接收一次,读入自己的数据区内
  3. m个缓冲区都满的时候,发送进程等待,没有可读的信息时,接收进程等待

提取信息:

发送进程只发送一次,接收进程取n2次,假设缓冲区长度为n2

设置信号量

mutex=1用来实现对缓冲区的互斥访问

empty[i] (i=1,…n2),初值为m,每个empty[i]对应缓冲池第i格中的所有空闲缓冲区

full[i] (i=1…n2) 初值为0,对应缓冲区第i格中装有消息的缓冲区。

提供整形变量in指示将消息放入哪个缓冲区

out[j] (j=1…n2)用来指示Bj从哪个缓冲区中取消息

Ai() {//i = 1...n1
    int k;
    while (1) {
        for (k = 1;k <= n2;k++) wait(empty[k]);//申请大小为n2的空闲分区
        wait(mutex);
    	把Ai的消息放入第in个缓冲区中
        in = (in + 1) % m;
        signal(mutex);
        for (k = 1;k <= n2;k++) signal(full[k]);
    } 
}

Bi() {
    while (1) {
        wait(full[j]);
        wait(mutex);
        从第out[j]个缓冲区的第j格中取出消息
       	out[j] = (out[j] + 1) % m;
        signal(mutex);
        signal(empty[j]);
        把数据写到消息区
    }
}

生产多个产品的生产者

桌上有个能盛的下五个水果的空盘子,爸爸不停向盘中放入苹果或橘子,儿子不停的从盘中取出橘子,女儿不停的从盘中取出苹果。规定三人不能同时往盘中取放水果,用信号量实现三个循环进程之间的同步

分析:

典型的生产者消费者问题,爸爸为能生产两个产品的生产者,缓冲区大小为5,需要使用mutex来互斥访问

semaphore mutex = 1,orange = 0,apple = 0,empty = 5;
void Father() {
 	while (1) {
        wait(empty);
        wait(mutex);
        //将水果放入盘中
        signal(mutex);
        if (放的橘子) {
            signal(orange);
        }else{
            signal(apple);
        }
    }
}

void Son() {
    while (1) {
        wait(orange);
        wait(mutex);
        取出橘子
        signal(mutex);
        signal(empty);
        吃了
    }
}

void Daughter() {
    while (1) {
        wait(apple);
        wait(mutex);
        取出苹果
        signal(mutex);
        signal(empty);
        吃了
    }
}

多个生产者+限制

设有两个生产者进程A、B和一个销售者进程C,它们共享一个无限大的仓库,生产者每次循环生成一个产品,然后入库供销售者销售;销售者每次循环从仓库中取出一个产品进行销售。如果不允许同时入库,也不允许边入库边出库;而且要求生产A产品和B产品的件数满足以下的关系:-n<=A的件数-B的件数<=m,其中n,m为正整数,但是对仓库中的A、B无限制,请用信号量机制写出A、B、C的工作流程

分析:

缓冲池:无限大的仓库,临界资源,使用mutex互斥

AB差值的约束,若A生产1份差值加一,B生产一份差值减一,则用信号量来控制这个差值:

SAB【sub A B】= m【允许A生产的产品数量】,SBA【sub B A】 = n【允许B生产的产品数量】

仓库无限大,但是C的销售需要仓库中的产品数量参考,设信号量S = 0代表仓库中初始产品个数

semaphore mutex = 1,SAB = m,SBA = n,S = 0;
A () {
    while (1) {
        //生产产品 
        wait(SAB);//生产一个A
        wait(mutex);
        signal(SBA);//B能生产的个数+1
        //放入仓库
        signal(S);
        signal(mutex);
    }
}
B () {
	while (1) {
        //生产产品
        wait(SBA);
        wait(mutex);
        signal(SAB);
        //放入仓库
        signal(S);
        signal(mutex);
    }
}

C() {
    while (1) {
		wait(S);
        wait(mutex);
        //取出仓库产品
        signal(mutex);
        //销售
    }
}

读写者变体

请出一种写者优先的读者写者问题的算法描述

在读者优先的算法上,增添一个初值为1的信号量S,使得当至少一个写者准备访问共享对象时,它可使后续的读者进程等待写完成;初值为0的writecount对写者进行计数;初值为1的互斥信号量wmutex,实现多个写着对writecount的互斥访问

reader() {
    while (1) {
        wait(S);
        wait(rmutex);
        if (readcount == 0) wait(mutex);
        readcount++;
        signal(rmutex);
        signal(S);
        //读
        wait(rmutex);
        readcount--;
        if (readcount == 0) signal(mutex);
        signal(rmutex);
    }
}

writer() {
    while (1) {
		wait(wmutex);
        if (writecount == 0) wait(rmutex);
        writecount++;
        signal(wmutex);
    	wait(mutex);
        //写
        signal(mutex);
        wait(wmutex);
        writecount--;
        if (writecount == 0) signal(S);
        signal(wmutex);
   	}
}

请用信号量解决以下的过独木桥问题:同一方向的行人可连续过桥,当某一方向有人过桥时,另一方向的行人必须等待,当某一方向无人过桥时,另一方向行人可以过桥

独木桥问题是读者写者问题的变形,相当于两种不同的读者,相同读者可以同时读,不同读者必须互斥

semaphore bridge = 1;//用来实现不同方向行人对桥的互斥
int countA,countB = 0;//AB两个方向过桥的人数
semaphore mutexA,mutexB = 1;//用来实现对countA、countB的互斥共享
void  PA() {
    wait(mutexA);
    if (countA == 0) wait(bridge);
    countA++;
    signal(mutexA);
    过桥
    wait(mutexA);
    countA--;
    if (countA == 0) signal(bridge);
    signal(mutexA);
}
//B的代码和A一致,都是读者格式代码

哲学家变体

有一间酒吧里有3个音乐爱好者队列,第一队的音乐爱好者只有随身听,第二队的音乐爱好者只有磁带,第三队的只有电池,而要听音乐必须三个俱全。酒吧老板一次出售三种中的任意两种。当一名音乐爱好者得到三种并听完一首乐曲后,98老板才能再次出手三种的两种,一直循环进行下去。

三种临界资源随身听、磁带、电池。

记磁带电池为S1,随身听电池S2,随身听磁带S3【两种物品必须被同一个音乐爱好者取走,将两个看作一个组合起来的临界资源】

一次最多只有一个完整的组合,听完后才能再次出售商品,记为music

semaphore S1 = S2 = S3 = 1,music = 0;
void fan1() {
	wait(S1);
    //购买磁带电池
    //听音乐
    signal(music)
}
void fan2() {
    wait(S2);
    //购买随身听电池
    //听音乐
    signal(music);
}
//fan3同理
void boss() {
    while (1) {
        //提供任意两种产品售卖
        if (卖的是磁带电池) {
            signal(S1);
        }else if (卖的是随身听电池){
            signal(S2);
        }else{
            Signal(S3);
        }
        wait(music);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值