2015-08-10 11 views
6

Secondo la Rust exercise docs, la loro attuazione mutex a base del problema Possibilità Filosofi evita deadlock selezionando sempre forcella ID basso in quanto la sospensione fianco di ogni filosofo, cioè, facendo mancini:Perché i filosofi della ristorazione non si esercitano nella situazione di stallo se vengono eseguiti in modo errato?

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

Tuttavia, se disobbedisco a questa regola e cambio gli indici delle forcelle nell'ultimo Philosopher, il programma viene comunque eseguito senza deadlocking o panico.

Altre cose che ho provato:

  • Allungando l'argomento dormono nella eat() funzione di chiamata
  • Commentando l'argomento sonno
  • avvolgimento del corpo principale in un loop{} per vedere se sarebbe accaduto alla fine

Cosa devo fare per rompere correttamente?


Qui è la fonte completa senza nessuna delle modifiche di cui sopra:

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: Purtroppo l'attuale documentazione ruggine non comprendono questo esempio, in modo che il link qui sopra è rotto.

risposta

8

Lo stallo si verifica quando ogni filosofo "simultaneamente" prende la forchetta alla sua sinistra, e poi trova che la forchetta alla sua destra è già occupata. Per far sì che ciò accada spesso in modo non trascurabile, è necessario introdurre un fattore fudge nella "simultaneità", in modo che se i filosofi raccolgono le loro forchette sinistre entro una certa quantità di tempo l'una dall'altra, che nessuno di loro sarà capace per prendere le loro forchette giuste. In altre parole, è necessario introdurre un po 'di sonno tra raccogliendo le due forchette:

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

(Naturalmente, questo non garanzia una situazione di stallo, rende solo molto più probabile.)

+0

Meraviglioso. Questo si blocca subito e l'inversione degli indici impedisce definitivamente che si verifichi una situazione di stallo. –

+0

Brillante e semplice. Questo dovrebbe essere copiato nella documentazione – tafia