|
| 1 | +/* |
| 2 | +* The dining philosophers problem involves multiple threads needing |
| 3 | +* synchronized access to shared resources, risking deadlock. |
| 4 | +* |
| 5 | +* This code models philosophers as threads and forks as shared Mutex<()> |
| 6 | +* wrapped in Arc for thread-safe reference counting. |
| 7 | +* |
| 8 | +* To prevent deadlock from a "deadly embrace" of waiting for neighboring |
| 9 | +* forks, philosophers acquire lower numbered forks first. This breaks |
| 10 | +* symmetry and avoids circular waiting. |
| 11 | +* |
| 12 | +* The Mutexes provide exclusive fork access. The Arc allows sharing forks |
| 13 | +* between philosophers. |
| 14 | +* |
| 15 | +* The simulation prints start time, eating duration, and total time for |
| 16 | +* all philosophers. Total time approximately equals philosophers divided |
| 17 | +* by forks, as that number can eat concurrently. |
| 18 | +* |
| 19 | +* Key techniques: |
| 20 | +* - Used Mutex<()> to represent exclusive fork access |
| 21 | +* - Wrapped in Arc to share Mutexes between threads |
| 22 | +* - Numbered philosophers and acquire lower fork first |
| 23 | +* - Prints timing metrics for simulation |
| 24 | +* |
| 25 | +* There is diminishing returns with concurrency timing is very important to |
| 26 | +* establish if threading is worth the overhead. |
| 27 | +*/ |
| 28 | + |
| 29 | +use std::sync::{Arc, Mutex}; |
| 30 | +use std::thread; |
| 31 | +use std::time::{Duration, Instant}; |
| 32 | + |
| 33 | +struct Fork { |
| 34 | + id: u32, |
| 35 | + mutex: Mutex<()>, |
| 36 | +} |
| 37 | + |
| 38 | +struct Philosopher { |
| 39 | + id: u32, |
| 40 | + name: String, |
| 41 | + left_fork: Arc<Fork>, |
| 42 | + right_fork: Arc<Fork>, |
| 43 | +} |
| 44 | + |
| 45 | +impl Philosopher { |
| 46 | + fn new(id: u32, name: &str, left_fork: Arc<Fork>, right_fork: Arc<Fork>) -> Philosopher { |
| 47 | + Philosopher { |
| 48 | + id, |
| 49 | + name: name.to_string(), |
| 50 | + left_fork, |
| 51 | + right_fork, |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + fn eat(&self) { |
| 56 | + let (first_fork, second_fork) = if self.id % 2 == 0 { |
| 57 | + (&self.left_fork, &self.right_fork) |
| 58 | + } else { |
| 59 | + (&self.right_fork, &self.left_fork) |
| 60 | + }; |
| 61 | + |
| 62 | + let _first_guard = first_fork.mutex.lock().unwrap(); |
| 63 | + println!("{} picked up fork {}.", self.name, first_fork.id); |
| 64 | + let _second_guard = second_fork.mutex.lock().unwrap(); |
| 65 | + println!("{} picked up fork {}.", self.name, second_fork.id); |
| 66 | + |
| 67 | + println!("{} is eating.", self.name); |
| 68 | + thread::sleep(Duration::from_secs(1)); |
| 69 | + println!("{} finished eating.", self.name); |
| 70 | + |
| 71 | + println!("{} put down fork {}.", self.name, first_fork.id); |
| 72 | + println!("{} put down fork {}.", self.name, second_fork.id); |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +fn main() { |
| 77 | + println!("Dining Philosophers Problem: 15 Philosophers, 4 Forks...Yikes!!"); |
| 78 | + |
| 79 | + //we only have 4 forks at the table |
| 80 | + let forks = (0..4) |
| 81 | + .map(|id| { |
| 82 | + Arc::new(Fork { |
| 83 | + id, |
| 84 | + mutex: Mutex::new(()), |
| 85 | + }) |
| 86 | + }) |
| 87 | + .collect::<Vec<_>>(); |
| 88 | + |
| 89 | + let philosophers = vec![ |
| 90 | + ("Jürgen Habermas", 0, 1), |
| 91 | + ("Friedrich Engels", 1, 2), |
| 92 | + ("Karl Marx", 2, 3), |
| 93 | + ("Thomas Piketty", 3, 0), |
| 94 | + ("Michel Foucault", 0, 1), |
| 95 | + ("Socrates", 1, 2), |
| 96 | + ("Plato", 2, 3), |
| 97 | + ("Aristotle", 3, 0), |
| 98 | + ("Pythagoras", 0, 1), |
| 99 | + ("Heraclitus", 1, 2), |
| 100 | + ("Democritus", 2, 3), |
| 101 | + ("Diogenes", 3, 0), |
| 102 | + ("Epicurus", 0, 1), |
| 103 | + ("Zeno of Citium", 1, 2), |
| 104 | + ("Thales of Miletus", 2, 3), |
| 105 | + ] |
| 106 | + .into_iter() |
| 107 | + .enumerate() |
| 108 | + .map(|(id, (name, left, right))| { |
| 109 | + Philosopher::new( |
| 110 | + id as u32, |
| 111 | + name, |
| 112 | + Arc::clone(&forks[left]), |
| 113 | + Arc::clone(&forks[right]), |
| 114 | + ) |
| 115 | + }) |
| 116 | + .collect::<Vec<_>>(); |
| 117 | + |
| 118 | + let start = Instant::now(); |
| 119 | + |
| 120 | + let handles = philosophers |
| 121 | + .into_iter() |
| 122 | + .map(|philosopher| { |
| 123 | + thread::spawn(move || { |
| 124 | + philosopher.eat(); |
| 125 | + }) |
| 126 | + }) |
| 127 | + .collect::<Vec<_>>(); |
| 128 | + |
| 129 | + for handle in handles { |
| 130 | + handle.join().unwrap(); |
| 131 | + } |
| 132 | + |
| 133 | + println!("Total time: {:?}", start.elapsed()); |
| 134 | +} |
0 commit comments