【操作系统导论】第30章课后习题解答

本文详细解析了多线程环境下生产者消费者问题的解决方案,涉及main-two-cvs-while.c等代码实例,讨论了不同场景下生产者/消费者数量、buffer大小对并发行为的影响,以及条件变量、if判断和额外解锁带来的问题。
摘要由CSDN通过智能技术生成

前言

内容仅作记录,请谨慎参考。

30.1

题目描述

在这里插入图片描述

分析及解答

查看main-two-cvs-while.c的代码,对代码功能进行分析
通过代码中的四个方法:do_fill()、do_get()、producer()、comsumer()我们可以知道,这是使用条件变量解决生产者/消费者的方案。
在这里插入图片描述

在do_fill()方法中,指针始终指向尚未填充的空闲空间,调用该方法对应的就是“生产”的过程。

在这里插入图片描述

do_get()对应“消费”的过程,进程调用该方法时,数据将会被取出。

在这里插入图片描述

在上面的代码中,生产者线程等待条件变量empty,发信号给fill

在这里插入图片描述

在上面的代码中,消费者线程等待条件变量fill,发信号给empty,使用了两个条件变量,这样,消费者不会再唤醒消费者,生产者也不会再唤醒生产者。

30.2

题目描述

在这里插入图片描述

分析及解答

首先,设置生产者数量为1,消费者数量为1,同时buffer的大小为1,让生产者生产产生一些数据,观察生产与消费的情况,之后增大buffer的大小,再进行观察
首先在程序文件夹下使用make命令对程序按照给定规则进行编译
[有警告信息但不影响运行]
在这里插入图片描述
在这里插入图片描述
按照命令:./main-two-cvs-while -p 1 -c 1 -m 1 -l 3 -v运行程序
这里对参数选项进行解释:
-p x表示指定生产者数量为x
-c x表示指定消费者数量为x
-m x表示指定buffer大小为x
-l x表示生产多少
-v跟踪运行的过程
这里指定生产者和消费者的数量都为1,buffer的大小为1,生产者生产总数为3
运行查看结果:

buffer大小为1时
在这里插入图片描述
buffer大小为2时
在这里插入图片描述
buffer大小为3时
在这里插入图片描述

可以看到对于不同大小的buffer,每次运行的结果并非都是相同的,这是因为线程的调度顺序并不是固定的,有可能生产者线程先得到调度,消费者线程后得到调度,也可能相反;当改变buffer的大小时,对于生产者来说,每次可能进行的生产循环上限将增加,但总体上,当生产数量不变时,生产与消费的数量都是不变的。
当将buffer的大小设置为10(即-m 10),生产的总数设置为100(即-l 100),同时设置消费者的睡眠情况为-C 0,0,0,0,0,0,1(也就是在c6处进入睡眠),根据上述分析可以知道,在生产者线程,可以生产的最大数量为10,所以我们可以预测,处于生产者线程时,最大能生产的数量为10,而处于消费者线程时,每次最多消费1个数据,因为每次到达c6必须进入睡眠,无论队列中是否消费完。
命令为:
main-two-cvs-while -p 1 -c 1 -m 10 -l 100 -C 0,0,0,0,0,0,1 -v
程序运行结果比较长,在此不再贴出。

30.4

题目描述

在这里插入图片描述

分析及解答

输入命令:
./main-two-cvs-while -p 1 -c 3 -m 1 -C 0,0,0,1,0,0,0:0,0,0,1,0,0,0:0,0,0,1,0,0,0 -l 10 -v -t
运行程序查看模拟的结果[由于输出非常长,这里切开来看]
在这里插入图片描述

由题目我们知道,设置了3个消费者,每个消费者在c3的时候进入睡眠,睡眠的时长为1秒,以上面运行出的结果为例[每次运行结果可能不一样],对花费的总时长进行分析,本次运行率先运行的是消费者线程,由于buffer中没有可以取的数据,所以三者在c2的时候进入等待fill信号的状态,之后生产者线程进行数据的填充(“生产”),当buffer中有数据的时候,消费者就可以继续运行了,但每次到达c3都要睡眠1秒钟,可以看到整个过程中,3个消费者总共到达c3的次数为13次,所以总睡眠时长为13秒,由于程序运行本身花费时间相比较13秒来说非常的小,所以程序总共花费时间近似为13秒。

查看结果:
在这里插入图片描述
可以看到结果与分析一致,程序总花费时间近似13秒。

30.8

题目描述

在这里插入图片描述

分析及解答

首先分析一下main-one-cv-while,c代码:
在这里插入图片描述

根据毕生所学可以知道,这种生产者/消费者方案存在的问题在于“只使用了一个条件变量”,
但是当消费者数量为1时,消费者每次只能唤醒生产者,避免了这个问题,所以当生产者和消费者的数量都为1时,这种方案没有问题

30.9

题目描述

在这里插入图片描述

分析及解答

与第8题不同,这次有两个消费者,那么这种“只使用一个条件变量”的方案就会出现显著的问题和漏洞:

假设存在一个生产者和两个消费者,两个消费者先运行,之后都睡眠了,生产者开始运行,并在缓冲区放入了一个值,唤醒了两个睡眠中的消费者线程中的一个,生产者进入睡眠,被唤醒的消费者把这个值消费掉,之后发出信号,唤醒一个线程,该消费者进入睡眠,当唤醒的是消费者的时候,发现buffer为空就又会进入睡眠,结果最后3个线程全部进入睡眠。

根据以上分析,我们可以构造一个让该问题突出的睡眠序列:
./main-one-cv-while -p 1 -P 0,0,0,1000,0,0,1000 -c 2 -m 1 -C 0,0,0,1000,0,0,1000:0,0,0,1000,0,0,1000 -l 2 -v

解释上述的序列含义:
理论上“睡眠”状态,在被唤醒之前是不会醒过来的,为了模拟这种效果,把每次睡眠的时长设置为1000,对于生产者,当buffer已满(p3)和生产过程结束时(p6)将进入睡眠状态,所以在p3和p6处设置睡眠,对于消费者,当buffer为空(c3)和消费过程结束时(c6)进入睡眠状态,所以在c3和c6处设置睡眠,-l 2表示生产总数为2

运行结果如下:
在这里插入图片描述

可以看到程序首先运行生产者线程P1,生产过程结束之后进入睡眠,C1线程开始运行,消费结束之后唤醒C0线程,C0线程发现buffer为空,进入睡眠,结果3个线程都进入了睡眠状态,与上述分析结果一致。
[当然,这是用睡眠串来故意构造的来让问题更显著,这种睡眠使用main-two-cvs-while是不能唤醒的]

30.10

题目描述

在这里插入图片描述

分析及解答

首先分析一下main-two-cvs-if的代码:
在这里插入图片描述

根据毕生所学可以知道,这种生产者/消费者方案存在的问题在于“使用if语句判断num_fill”,假设有一个生产者和两个消费者,第一个消费者Tc1首先调度,发现无数据可取,进入睡眠,之后运行生产者线程,生产者将buffer填满,唤醒Tc1,进入睡眠,这时Tc2如果插入运行,消费了buffer中的数据,Tc1再继续运行时将发现无数据可取!
根据以上分析,我们可以构造一个让该问题突出的睡眠序列:

./main-two-cvs-if -p 1 -P 0,1,1,1,0,0,1 -c 2 -m 1 -C 0,0,0,1,0,0,1:0,0,0,1,0,0,1 -l 4 -v
运行结果如下:
在这里插入图片描述
可以看到当一个消费者将buffer中的数据消费之后,另一个消费者再次尝试获取数据,将会发现无数据可取,导致断言触发。

30.11

题目描述

在这里插入图片描述

分析及解答

首先分析一下main-two-cvs-while-extra-unlock的代码:
在这里插入图片描述

这个代码的问题与上面第10题类似,再put()或get()就把锁打开,可能导致别的线程插入进来运行,导致该线程再次get()的时候结果无数据可取,导致断言触发!

根据以上分析,我们可以构造一个让该问题突出的睡眠序列:
./main-two-cvs-while-extra-unlock -p 1 -P 0,0,1,1,0,0,1 -c 2 -m 1 -C 0,0,1,1,0,0,1:0,0,1,1,0,0,1 -l 4 -v
[这个构造不能每次都产生问题,要多运行几次,发生另一个线程插入运行的情况]
运行结果如下:
在这里插入图片描述
可以看到当Tc0将要进行get()的时候,Tc1插入运行,Tc0再次运行时没有数据可取,导致断言触发!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼树C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值