2016-04-15 29 views
5

Ho codice che crea un RefCell e poi vuole passare un riferimento a tale RefCell ad una discussione singolo:Come posso garantire che un tipo che non implementa Sync possa effettivamente essere condiviso in modo sicuro tra i thread?

extern crate crossbeam; 

use std::cell::RefCell; 

fn main() { 
    let val = RefCell::new(1); 

    crossbeam::scope(|scope| { 
     scope.spawn(|| *val.borrow()); 
    }); 
} 

Nel codice completo, sto usando un tipo che ha un RefCell incorporato in it (a typed_arena::Arena). Sto usando crossbeam per assicurarmi che il thread non sopravviva al riferimento che ci vuole.

Questo produce l'errore:

error: the trait bound `std::cell::RefCell<i32>: std::marker::Sync` is not satisfied [E0277] 

    scope.spawn(|| *val.borrow()); 
      ^~~~~ 

Credo di capire perché questo errore accade: RefCell non è progettato per essere chiamato contemporaneamente da più thread, e poiché usa mutabilità interno, il normale meccanismo di richiedere un il singolo prestito mutabile non impedirà più azioni simultanee. Questo è anche documentata sul Sync:

Types that are not Sync are those that have "interior mutability" in a non-thread-safe way, such as Cell and RefCell in std::cell .

Questo è cosa buona e giusta, ma in questo caso , so che solo un thread è in grado di accedere al RefCell. Come posso affermare al compilatore che capisco quello che sto facendo e mi assicuro che sia così? Certo, se il mio ragionamento sul fatto che questo è effettivamente sicuro non è corretto, sarei più che felice di sentirmi dire perché.

risposta

2

Bene, un modo sarebbe quello di utilizzare un involucro con una unsafe impl Sync:

extern crate crossbeam; 

use std::cell::RefCell; 

fn main() { 
    struct Wrap(RefCell<i32>); 
    unsafe impl Sync for Wrap {}; 
    let val = Wrap(RefCell::new(1)); 

    crossbeam::scope(|scope| { 
     scope.spawn(|| *val.0.borrow()); 
    }); 
} 

Quindi, come al solito con unsafe, è ora a voi per garantire che l'interno RefCell è infatti mai accedere da molteplici fili contemporaneamente. Per quanto ho capito, questo dovrebbe essere sufficiente per non causare una corsa di dati.

6

Un'altra soluzione è spostare un riferimento mutabile all'elemento nel thread, anche se non è richiesta la mutevolezza. Dato che può esserci solo un riferimento mutabile, il compilatore sa che è sicuro essere usato in un altro thread.

extern crate crossbeam; 

use std::cell::RefCell; 

fn main() { 
    let mut val = RefCell::new(1);  
    let val2 = &mut val; 

    crossbeam::scope(|scope| { 
     scope.spawn(move || *val2.borrow()); 
    }); 
} 
+2

Ciò è consentito perché 'RefCell ' implementa 'Invia'. – bluss