Why do lunch philosophers not make dead ends if they are done wrong?

In accordance with Rust exercise docs , their implementation on the mutex-based philosophers ’dining room avoids deadlocks by always choosing the smallest ID fork as the left fork of every philosopher, i.e. making one left:

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), ]; 

However, if I do not obey this rule and change the fork indices in the latest Philosopher , the program still works without blocking or panic.

Other things I've tried:

  • Extending the sleep argument when calling the eat() function
  • Commenting out the sleep argument
  • Wrap the main body in loop{} to make sure this happens in the end.

What do I need to do to break it right?


Here is the complete source without any changes:

 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(); 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(); } } 

PS: Unfortunately, current Rust docs do not include this example, so the link above does not work.

+6
source share
1 answer

A dead end occurs when each philosopher “simultaneously” lifts the plug on his / her left side, and then discovers that the plug has already been removed on his / her right. In order for this to happen not negligibly often, you need to introduce some invention factor into “simultaneity”, so that if philosophers all take their left forks for a certain period of time from each other, that none of them can lift their right forks. In other words, you need to sleep a little between two forks:

  fn eat(&self, table: &Table) { let _left = table.forks[self.left].lock().unwrap(); thread::sleep_ms(1000); // <---- simultaneity fudge factor let _right = table.forks[self.right].lock().unwrap(); println!("{} is eating.", self.name); thread::sleep_ms(1000); println!("{} is done eating.", self.name); } 

(Of course, this does not guarantee a dead end, it just makes it much more likely.)

+8
source

All Articles