我正试图跟随Rust文档中的餐饮哲学家的例子.链接的最终代码:
use std::thread; use std::sync::{Mutex, Arc}; struct Philosopher { name: String, left: usize, right: usize, } impl Philosopher { fn new(name: &str, left: usize, right: usize) -> Philosopher { Philosopher { name: name.to_string(), left: left, right: right, } } fn eat(&self, table: &Table) { let _left = table.forks[self.left].lock().unwrap(); thread::sleep_ms(150); let _right = table.forks[self.right].lock().unwrap(); println!("{} is eating.", self.name); thread::sleep_ms(1000); println!("{} is done eating.", self.name); } } struct Table { forks: Vec>, } fn main() { let table = Arc::new(Table { forks: vec![ Mutex::new(()), Mutex::new(()), Mutex::new(()), Mutex::new(()), Mutex::new(()), ]}); let philosophers = vec![ Philosopher::new("Judith Butler", 0, 1), Philosopher::new("Gilles Deleuze", 1, 2), Philosopher::new("Karl Marx", 2, 3), Philosopher::new("Emma Goldman", 3, 4), Philosopher::new("Michel Foucault", 0, 4), ]; let handles: Vec<_> = philosophers.into_iter().map(|p| { let table = table.clone(); thread::spawn(move || { p.eat(&table); }) }).collect(); for h in handles { h.join().unwrap(); } }
运行此命令会产生以下输出:
Michel Foucault is eating. Michel Foucault is done eating. Emma Goldman is eating. Emma Goldman is done eating. Karl Marx is eating. Karl Marx is done eating. Gilles Deleuze is eating. Gilles Deleuze is done eating. Judith Butler is eating. Judith Butler is done eating.
根据文件,哲学家应该能够同时吃饭.期望的结果是这样的:
Gilles Deleuze is eating. Emma Goldman is eating. Emma Goldman is done eating. Gilles Deleuze is done eating. Judith Butler is eating. Karl Marx is eating. Judith Butler is done eating. Michel Foucault is eating. Karl Marx is done eating. Michel Foucault is done eating.
不幸的是,无论代码执行的频率如何,都不会发生这种情况.
我目前正在rustc 1.5.0 (3d7cd77e4 2015-12-04)
Windows上使用,但问题也发生在Rust操场上.随意尝试一下.
由于拣货叉之间的睡眠,问题的实施和建议的输出不匹配.
我不确定为什么Michel Foucault总是首先开始(可能是线程调度的方式),但其余部分很容易解释.
由于抓住主手叉和副手叉之间的暂停(*),有两个阶段:
阶段1:抓住你的主叉
阶段2:抓住你的副手叉
第1阶段后:
Fork 0掌握在Michel Foucault或Judith Butler手中
Fork 1在Gilles Deleuze的手中
Fork 2掌握在Karl Marx手中
Fork 3掌握在Emma Goldman手中
现在,请注意,只有Fork 4可供抓取!
我们在第2阶段有两个案例:
a)朱迪思抓住了叉子0 b)米歇尔抓住了叉子0
从(a)开始:
除了抓住Fork 4的Emma之外,所有的哲学家都被封锁了
当Emma完成后,她释放了Fork 3,Karl立刻抓住了它
当卡尔完成......
最后,Judith完成了,她释放了Fork 0,而Michel吃了
在(a)的情况下,只有一个哲学家可以在任何给定的时间吃东西.
注意:我强迫这个案子暂停了Michel 150ms,然后让他抓住他的第一个叉子.
案例(b)更加复杂,因为我们再次参加比赛,这次是艾玛和米歇尔之间的比赛,我们是绅士,所以艾玛会先走,米歇尔抓住福克斯4的案子现在被命名为(c) :
艾玛抓住了福克斯4号,所有其他哲学家现在都被封锁了
当艾玛完成后,她释放了第3和第4分叉,米歇尔和卡尔都跳了起来
当米歇尔完成后,他释放福克斯0和4,朱迪思立即抓住它......然后开始等待; 现在没有人关心Fork 4
当Karl完成后,他释放了Fork 2,Gilles立即抓住了
当Gilles完成后,他释放了Fork 1,Judith立即抓住了
当朱迪思完成后,所有5人都吃完了
我们观察到非常有限的并发性:Emma首先命中,只有当她完成时我们才有两个并行流,一个是Michel,一个是Karl> Gilles> Judith.
注意:我强迫这个案子暂停了Michel 150ms,然后让他抓住他的第二个叉子.
最后,我们有案例(c):
米歇尔抓住了福克斯4号,所有其他哲学家现在都被封锁了
当米歇尔完成后,他释放了分叉4和0,分别由艾玛和朱迪思抓住; 朱迪思仍然被封锁(第一次睡觉,然后等待福克斯1),但艾玛开始吃东西
当艾玛完成时......
而且在这里,根本没有并发性.
(*)这实际上并没有保证,但150ms是计算机方面的长时间,除非机器非常负载,它才会发生.
虽然本书提出的解决方案确实有效(在任何情况下都没有死锁),但它并没有表现出太多的并发性,因此它更像是Rust的展示而不是并发展......但是,它是Rust书而不是并发的!
我不明白为什么Michel的主题是先在围栏上系统安排的; 但是可以通过让他专门睡觉来轻松应对.