习题1
哲学家就餐问题是由并发处理的先驱E.W.Dijkstra所提出,主要用于阐述死锁和无饥饿概念。假设五个哲学家一生只在思考和就餐。他们围坐在一个大圆桌旁,桌上有一大盘米饭。然而只有五根可用的筷子。所有的哲学家都在思考。若某个哲学家饿了,则拿起身边的两根筷子。如果他能够拿到这两根筷子,则可以就餐。当这个哲学家吃完后,又放下自己生变的两根筷子。如果他能够拿到这两根筷子,则可以就餐。当这个哲学家吃完后,又放下筷子继续思考。
1. 试编写模仿哲学家就餐行为的程序,其中给每一个哲学家为一个线程而筷子则是共享对象。注意,必须防止出现两个哲学家同时使用一根筷子的情形。
2. 修改所编写的程序,不允许出现死锁情况,也就是说,保证不会出现这样的情况:每个哲学家都已拥有一根筷子,并且在等待获得另一个人手中的筷子。
3. 修改所编写的程序使得不会出现饥饿现象。
4. 编写能够保证任意n个哲学家无饥饿就餐的程序。
解答:
这里筷子属于两个哲学家的共享资源,所以在代码中,定义为全局变量。
之后的思路就简单了,这里的锁相关函数没有实现,这里就当做伪代码来看吧。
一个哲学家需要先看左手边的筷子,当左边的筷子没有人使用时拿起。
右边同理。
这时,哲学家有三个状态。
①没有拿着筷子
②拿到了一根筷子
③幸运的拿到了两根筷子
这时,当处于③状态时,就可以开心的吃米饭了。吃完以后,将筷子放下,让别人使用。
当处于②状态时,一直拿着这根筷子会制造饥饿。所以,在看到只能拿到一只筷子时,就再将它放下,可供其他人使用。
①状态,就只能等两边的人把筷子放下来了。
这个过程应该就能满足以上四个条件了。
int Chopsticks[poilosophers]; //需要提前知道哲学家的人数,用动态数组在指定的初始化函数进行分配也行
void selectChopsticks(int poilosopherId, int poilosophers){
int l_chopstick = poilosopherId, r_chopstick = poilosopherId + 1;
if (r_chopstick == poilosophers){
r_chopstick -= poilosophers; //保证环状
}
int getChopstickNum = 0, eated = 0;
while (1){
lock();
if (Chopsticks[l_chopstick] == 0){
Chopsticks[l_chopstick] = poilosopherId;
}
unlock();
lock();
if (Chopsticks[r_chopstick] == 0){
Chopsticks[r_chopstick] = poilosopherId;
}
unlock();
if (Chopsticks[l_chopstick] = poilosopherId && Chopsticks[r_chopstick] == poilosopherId) {
eated = 1;
}
if (Chopsticks[l_chopstick] == poilosopherId){
Chopsticks[l_chopstick] = 0;
}
if (Chopsticks[r_chopstick] == poilosopherId){
Chopsticks[r_chopstick] = 0;
}
if (eated) break;
}
}
习题2
下面各种方法满足安全性还是活性?指出所关心的“坏事”和“好事”。
1. 按到达顺序为赞助人服务。
2. 升上去的东西必须降下来。
3. 如果有两个或多个进程在等待进入自己的临界区,则至少有一个会成功。
4. 如果发生一个中断,则在一秒内输出一条信息。
5. 如果发生一个中断,则要输出一条信息。
6.生活费决不下降
7. 有两件事是肯定的:死亡和税
8.你总是能够告诉一个哈佛人。
解答:
1.活性,好事
2.活性,好事
3.活性,好事
4.安全性,坏事
5.活性,好事
6.安全性,坏事
7.活性,好事
8.活性,好事
这里…… 是我自行判断的,不确保正确性。翻译是没有问题的,这里可能是对一些假设做一些属性上判断。
习题3
在生产者-消费者问题中,假设Bob能够看到Alice窗台上的啤酒罐是竖直还是翻倒的。请基于啤酒罐-绳子协议来设计一种生产者-消费者协议,使得即使Bob无法看到Alice窗台上啤酒罐的状态,也能够正常工作(即实际中断位是如何工作的)。
解答:
因为没有办法看到啤酒罐的状态,所以无法判断宠物是否在院中。
不过,这里Bob肯定能看到食物槽中是否有食物。
所以,这里需要Bob依次做下面的事情:
①窗口看食物槽中是否还有食物
是:② 否:③
②进入院子,向食物槽中添加食物
③做会儿自己想做的事情,比如:看会电视。
④10~20分钟后,再做①
Alice同样需要做一些事情:
①检查食物是否吃完
是:② 否:③
②将宠物们召唤回屋,过一段时间再做①
③自行决定是否叫宠物们回屋,过段时间再做①
这里食物槽就是中断位,两人通过食物槽来决定自己的动作,从而避免书中提到的一些人与动物的冲突。
习题4
假如你是最近被捕的P个囚犯之一。监狱长是一个疯狂的计算机科学家,他给出了一下告示:
你们今天可以在一起商量一个策略,但是从今天之后,你们将会被隔开,关在不同的房间,互相间无法再进行交流。
我们已建造了一种“开关房间”,里面有一个灯开关,这个开关只能为开或关,且没有和任何东西相连。
我将不时的从你们中间随机选择一位到“开关房间”里来。这名囚犯可以拨动开关(从开到关,或相反),也可保持开关的状态不变。其他人这是都不能进入房间。
每一名囚犯都将任意多次地进入开关房间。更确切的说,对于任意的N,你们中的每个人最终至少能进入这个房间N次。
任何时刻,任意一名囚犯都可以宣布:"我们所有的人都已经至少到过开关房间一次了。"如果该断言是对的,我将释放你们。如果错了,我就把你们全部送去喂鳄鱼。谨慎抉择吧!
(1)在开关初始状态为 关 的情况下,设计一个可以成功取胜的策略。
(2)在不知道开关初始状态的情况下,设计一个可以成功取胜的策略。
解答:
其实①就是一个特解,这里将两个方式结合在一起来说。
这里,可以由第一个进入房间的人进行统计。
但这里就有个问题,如何确定这第一个人是谁?这个很难确定。
不过问题并非无解,在一天商量的时间内,我们大家定了了协议:
①在看到开关屋开关为 关 的状态时,开始计数。
②在第一次看到开关为 开 的状态时,拨动,将其换为 关。
③当其中某人计数达到P时,召唤监狱长。
在(1)中,因为我们知道初始状态为关,直接满足我们的协议。
而(2)中,我们则需要考虑开的情况,从而确定第一个人。
当初始状态为开时,第一个进去的人会将其关闭,第二个人进去时的情况就是①的情况了。
感觉也没有什么难得,就是获得释放的时间可能要迟一些了。
类似的问题还有很多有趣的讨论,有兴趣的同学可以去这些地方看看:
http://tieba.baidu.com/p/625765933
习题5
上题中的监狱长又有了另外一个想法。他命令囚犯站成一排,每个人都戴一顶红色或蓝色的帽子。没有人知道自己所戴帽子的颜色,也不知道他后面所有人帽子的颜色,但能看到前面所有人帽子的颜色。监狱长从队伍的后面开始询问每个囚犯,让他们猜测自己帽子的颜色。囚犯们只能回答“红色”或“蓝色”。如果他打错了,就会送去喂鳄鱼。如果他答对了,则会释放。每个囚犯都能听到后面所有人的回答,但不知道答案是否正确。
囚犯们在站队只前可以商讨一个策略(监狱长是听着的)。一旦站好队之后,每个人除了能回答“红色”和“蓝色”以外,再无法以任何形式进行交流。
设计一个能够保证P个囚犯中至少有P-1个会释放的策略。
解答:
第一眼看到这个题,就想起看耶鲁的博弈课中的那个场景,不过对比本题来说是两个问题。
这个题着实难解…… 我也是查了很多资料才找到一个符合题目的算法。
一下就来说说这个算法:
因为能听到后面人的回答,并能看到前面人头上帽子的颜色。
这样的话,可以通过帽子的奇偶性来进行解决。
在商量的时候,我们建立这样协议:
①数出自己前面带蓝帽子人的数量(为B)
②记住自己后面人回答“蓝色”的数量(为b)
③将B与b进行加和。和为偶数的时候,回答“蓝色”;和为奇数的时候,回答“红色”。
表达式也写一下:
B = blueAhead = 前面带蓝帽子的数量
b = blueBehind = 后面回答“蓝色”的数量
if B + b 是偶数:
回答 "蓝色"
else
回答 “红色”
这里验证一下,举个两个例子:
这里用“颜色-序号”来代表一个囚徒的帽子颜色和提问顺序(从小到大)
①
【红-0 】【蓝-1】【 红-2】【 红-3】【 蓝-4】【 红-5】【蓝-6】【蓝-7】
0)4 + 0 = 4:蓝(死亡)
1)3 + 1 = 4:蓝(存活)
2)3 + 2 = 5:红(存活)
3)3 + 2 = 5:红(存活)
4)2 + 2 = 4:蓝(存活)
5)2 + 3 = 5:红(存活)
6)1 + 3 = 4:蓝(存活)
7)0 + 4 = 4:蓝(存活)
共8人,存活7人。
②下面多加几个人,看看结果
【红-0 】【蓝-1】【 红-2】【 红-3】【 蓝-4】【 红-5】【蓝-6】【蓝-7】 【红-8】【红-9】【蓝-10】
0)5 + 0 = 5:红(存活)
1)4 + 0 = 4:蓝(存活)
2)4 + 1 = 5:红(存活)
3)4 + 1 = 5:红(存活)
4)3 + 1 = 4:蓝(存活)
5)3 + 2 = 5:红(存活)
6)2 + 2 = 4:蓝(存活)
7)1 + 3 = 4:蓝(存活)
8)1 + 4 = 5:红(存活)
9)1 + 4 = 5:红(存活)
10) 0 + 4 = 4:蓝(存活)
共10人,存活10人。
有兴趣的同学可以去这里看看:
how-many-of-a-hundred-hatted-prisoners-can-save-themselves-from-the-king
习题6
使用Amdahl定律解决下面问题:
①假定在一个程序中包含有一个无法并行化的方法M,其执行时间该为程序总时间的40%,若在一台n处理器的多核机器上运行此程序,总加速比的上限应为多少?
②假定方法M占整个程序执行时间的30%。要使程序的总运行时间比原来提高2倍,M的加速比应为多少?
③假定方法M的速度可以提高3倍。要使程序的总加速比为原来的2倍,那么在程序的总运行时间中,M应占多大的比例?
解答:
①S = 1/(0.4 + 0.6/n) = n/(0.4n + 0.6),当n趋近与无穷,S = 1/0.4 = 2.5。所以,上限为2.5
②这个应该是用加速比来反推p的值,然后再对M的加速比进行计算。
将原式变形,得p =[(1-S)n]/[(1-n)S]。
带入S=2,得p = n/[2(n -1)] => 1-p = (n - 2)/[2(n-1)]。
这时计算出在新程序中M的运行时间为 1/2 * (1 - p) = (n - 2)/ [4(n - 1)]
计算加速比P =0.3/(1-p) = [6(n-2)]/[5(n-1)]。
当n趋近于无穷时,P=1.2
③这里我假设原始程序执行的时间是1,也就是M没有提高3倍前,然后加速比就是1.
当M提速3倍后,程序执行的时间是1/2,加速比也就是2了。
根据定律可得 2 = 1 / (1-p'+p'/n)=>p'=n/[2(n-1)]。
当n趋近于无穷时,p'=0.5
也就是M应占50%的比例。
以下是我找到的一些参考资料,因为没有看太懂batch那个,所以不确定,这里的回答是否正确。
无论正确与否吧,希望能给其他同学提供一个思路。
https://github.com/schuay/advanced_multiprocessor_programming/blob/master/exercises_batch_1/exercises.tex
https://groups.google.com/forum/#!topic/art-of-multiprocessor-programming/adGb3ifKNXI
习题7
在两个处理器上运行时,程序可获得加速比为S2。使用Amdahl定律推导出在n个处理器上运行时程序的加速比Sn,要求用n和S2来表示。
解答:
这里根据S2反推p,然后再计算Sn。
S2 = 1/(1-p+p/2) => p = 2(1-1/S2)
总时间T = 1 - p + p/n = 1 - p(1 - 1/n) = 1 - 2(1-1/S2)(1-1/n)
因为Sn = 1/T,所以Sn = 1 / [1 - 2(1-1/S2)(1-1/n)]
习题8
现有一台每秒可执行5亿万条指令的单处理器和一台有10个处理器的多处理器机器,其中每个处理器每秒可执行1亿万条指令。针对一个特定的应用,使用Amdahl定律来解释应该购买哪台机器。
解答:
对Amdahl定律进行变形,S = 1 / (1 - p + p/n) = 1 / [1 - p(1 - 1/n)]。
从这个等式中可以看出来,加速比与p有绝对的关系。
当p趋近与0时,其加速比就为1,也就是没有任何加速
当p趋近于1时,其加速比就是n,完全进行并行加速。
具体的例子可以参考书中粉刷房屋的例子。
不过,在这个题目中,再完全并行的情况下,其能执行的指令数并不一样。
那这里就需要有个讨论:
①10个处理器不完全使用的情况
②应用的指令数小于5亿万条。
这里②的情况比较简单,这里p无论是多少,我都会选择第一台机器。
而①的情况就有些复杂,根据等式其依旧有加速比(这里不去分割1s内具体指令的执行情况),也就是肯定要比第一台机器用的时间少。
所以,应该还会选择第二台机器。