Concurrency Rust文档中的用餐哲学家不同时吃饭
我正试图按照锈迹文件中的说明进行操作。链接中的最终代码:Concurrency Rust文档中的用餐哲学家不同时吃饭,concurrency,rust,Concurrency,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 {
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<Mutex<()>>,
}
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();
}
}
根据文献记载,哲学家应该能够同时吃饭。预期结果如下所示:
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.
不幸的是,无论代码执行的频率有多高,这种情况都不会发生
我目前正在Windows上使用
rustc 1.5.0(3d7cd77e4 2015-12-04)
,但问题也发生在生锈的操场上。请随意。由于拣货叉之间的休眠,问题的实现与建议的输出不匹配
我不确定为什么米歇尔·福柯总是先开始(可能是线程调度的工作方式),但其余的很容易解释
由于抓取主叉和非手叉之间的停顿(*),分为两个阶段:
- 第1阶段:抓住你的主手叉
- 第二阶段:拿起你的非手动叉子
- 叉子0在米歇尔·福柯或朱迪思·巴特勒手中
- 叉子1在Gilles Deleuze手中
- 叉子2在卡尔·马克思手中
- Fork 3在Emma Goldman手中
- 除了艾玛,所有的哲学家都被封锁了,她抓住了叉子
- 爱玛完成后,她松开叉子3,卡尔立即抓住叉子
- 卡尔做完后
- 最后,朱迪思完成了,她松开叉子,米歇尔吃了
- 艾玛抓起叉子4,其他所有的哲学家都被封锁了
- 当爱玛完成后,她松开了叉子3和4,米歇尔和卡尔都跳了上去
- 当Michel完成后,他释放了叉子0和4,Judith立即抓住它。。。开始等待;现在没人关心Fork 4了
- 卡尔完成后,他松开叉子2,吉尔立即抓住叉子
- 当Gilles完成后,他松开叉子1,Judith立即抓住叉子
- 朱迪思吃完后,5个人都吃了
- 米歇尔抓起叉子4,所有其他哲学家现在都被封锁了
- 当Michel完成后,他释放Fork 4和0,分别被Emma和Judith抓住;朱迪思仍然被堵住了(先是睡觉,然后等叉子1),但艾玛开始吃东西
- 当艾玛结束时
虽然这本书提出的解决方案确实有效(无论在什么情况下都不会出现死锁),但它并没有表现出太多的并发性,因此它更多的是一种生锈的表现,而不是并发性的表现。。。但是,这是铁锈书,而不是并发书
我不明白为什么Michel的线程被系统地排在playpen的第一位;但这很容易通过让他具体睡觉来应对。这是这个例子中的一个半常见问题。程序员倾向于认为线程是“随机的”,因为线程通常有不同的开始时间和运行长度。线程的大多数用法也不会在线程的整个生命周期中锁定共享资源。请记住,线程是确定性的,因为它们是由算法调度的 在本例中,主线程创建了一大堆线程,并将它们添加到操作系统管理的队列中。最终,主线程被调度程序阻塞或中断。调度程序查看线程队列并询问“第一个”线程是否可以运行。如果它是可运行的,那么它将运行一个时间片或直到它被阻塞 “第一个”线程由操作系统决定。例如,Linux有多个可调整的调度程序,允许您确定运行哪些线程的优先级。调度程序还可以选择提前或推迟中断线程 如果在线程的最开始添加打印,可以看到线程的开始顺序不同。下面是一个表,根据100次运行,哪个线程首先启动:
|职位|艾玛·戈德曼|吉尔·德勒兹|朱迪思·巴特勒|卡尔·马克思|米歇尔·福柯|
|----------+--------------+----------------+---------------+-----------+-----------------|
| 1 | 4 | 9 | 81 | 5 | 1 |
| 2 | 5 | 66 | 9 | 17 | 3 |
| 3 | 19 | 14 | 5 | 49 | 13 |
| 4 | 46 | 9 | 3 | 20 | 22 |
| 5 | 26 | 2 | 2 | 9 | 61 |
如果我正确地进行了统计,最常见的开始顺序是:
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.