gpt4 book ai didi

concurrency - Rust 文档中的进餐哲学家不会同时进餐

转载 作者:行者123 更新时间:2023-11-29 07:46:28 26 4
gpt4 key购买 nike

我正在尝试关注 dining philosophers example来自 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<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();
}
}

运行它会产生以下输出:

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.

不幸的是,无论代码执行的频率如何,这种情况都不会发生。

我目前在 Windows 上使用 rustc 1.5.0 (3d7cd77e4 2015-12-04),但在 Rust playground 上也会出现问题。欢迎调用try it yourself .

最佳答案

问题的实现和建议的输出不匹配,因为 fork 之间有休眠。

我不确定为什么 Michel Foucault 总是先开始(可能是线程调度的工作方式),但其余的很容易解释。

由于捕获主手叉和副手叉之间的暂停 (*),有两个阶段:

  • 第 1 阶段:捕获你的主手 fork
  • 第 2 阶段:拿起副手的 fork

第 1 阶段之后:

  • Fork 0 在 Michel Foucault 或 Judith Butler 手中
  • fork 1 在 Gilles Deleuze 的手中
  • Fork 2 在卡尔·马克思手中
  • Fork 3 在 Emma Goldman 手中

现在请注意,只有 Fork 4 可供抢购!

我们在第 2 阶段有两个案例:

a) Judith 捕获了 fork 0 b) Michel 捕获了 fork 0

从 (a) 开始:

  • 除 Emma 外,所有哲学家都被阻止,Emma 捕获了 Fork 4
  • Emma 完成后,她释放了 Fork 3,Karl 立即捕获了它
  • 当 Karl 完成后...
  • 最后,Judith 吃完了,她放出 fork 0,Michel 吃了

在情况 (a) 中,在任何给定时间只能有一个哲学家进餐。

注意:在让 Michel 拿起他的第一个 fork 之前,我暂停了 150 毫秒来强制这个案例。

案例 (b) 更加复杂,因为我们再次进行了一场比赛,这次是在 Emma 和 Michel 之间抢 Fork 4。我们是绅士,所以 Emma 会先走,Michel 抢 Fork 4 的案例现在被命名为(三):

  • Emma 捕获了 Fork 4,所有其他哲学家现在都被阻止了
  • Emma 完成后,她释放了 Fork 3 和 4,Michel 和 Karl 都跳上了它们
  • 当 Michel 完成后,他释放了 0 号和 4 号 fork ,Judith 立即捕获了它……并开始等待;现在没人关心 Fork 4
  • Karl 完成后,他释放了 Fork 2,Gilles 立即捕获了它
  • Gilles 完成后,他释放了 Fork 1,Judith 立即捕获了它
  • 当 Judith 吃完后,5 个人都吃完了

我们在这里观察到非常有限的并发性:Emma 先命中,只有当她完成时我们才有两个并行流,一个是 Michel,一个是 Karl > Gilles > Judith。

注意:在让 Michel 拿起他的第二把 fork 之前,我暂停了 150 毫秒来强制执行此操作。

最后,我们有情况 (c):

  • Michel 捕获了 Fork 4,所有其他哲学家现在都被阻止了
  • 当 Michel 完成后,他释放了 Fork 4 和 0,Emma 和 Judith 分别捕获了它们; Judith 仍然被阻塞(先是 sleep ,然后等待 Fork 1),但 Emma 开始吃东西
  • 艾玛完成后...

又一次,根本没有并发。

(*) 这实际上并不能保证,但 150 毫秒在计算机方面是一个很长的时间,除非机器负载很大,否则它就会发生。


虽然本书提出的解决方案确实有效(无论在何种情况下都不会出现死锁),但它并没有表现出太多的并发性,因此它更像是 Rust 的表现而不是并发性的表现……但是,它是Rust 书而不是并发书!

我不明白为什么米歇尔的线程被系统地安排在围栏上;但可以通过让他专门 sleep 来轻松应对。

关于concurrency - Rust 文档中的进餐哲学家不会同时进餐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34395981/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com