2016-04-15 2 views
5

У меня есть код, который создает RefCell, а затем хочет передать ссылку на этот RefCell к одного нити:Как я могу гарантировать, что тип, который не реализует Sync, может быть безопасно распределен между потоками?

extern crate crossbeam; 

use std::cell::RefCell; 

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

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

В полный код, я использую тип, который имеет встроенный в RefCell это (typed_arena::Arena). Я использую crossbeam, чтобы убедиться, что поток не переживает требуемую ссылку.

Это дает ошибку:

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

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

Я считаю, я понимаю, почему это происходит ошибка: RefCell не предназначен называться одновременно из нескольких потоков, и, поскольку она использует внутреннюю переменчивость, нормальный механизм требуя единственный измененный заимствование не предотвратит множественные одновременные действия. Это даже задокументировано на 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 .

Это все хорошо, но в этом случае, я знаю, что только один поток может получить доступ к RefCell. Как я могу подтвердить компилятору, что я понимаю, что делаю, и я гарантирую, что это так? Конечно, если мои рассуждения о том, что это на самом деле безопасно, неверны, я был бы более чем счастлив, если вам скажут почему.

ответ

2

Ну, один из способов будет использовать обертку с 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()); 
    }); 
} 

Так что, как обычно, с unsafe, теперь до вас, чтобы гарантировать, что внутренняя RefCell не действительно никогда не обращались с кратному потоков одновременно. Насколько я понимаю, этого должно быть достаточно, чтобы не вызвать гонку данных.

6

Другим решением является перемещение изменяемой ссылки на элемент в поток, хотя изменчивость не требуется. Поскольку может быть только одна измененная ссылка, компилятор знает, что ее можно использовать в другом потоке.

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

Это разрешено, потому что 'RefCell ' реализует 'Отправить'. – bluss