2016-04-15 1 views
5

私はRefCellを作成し、単一スレッドにそのRefCellへの参照を渡したいコードしている:、私はに埋め込まれRefCellを持っているタイプの完全なコードではSyncを実装していないタイプが実際にスレッド間で安全に共有できることを保証する方法を教えてください。

extern crate crossbeam; 

use std::cell::RefCell; 

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

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

使用していますそれは(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 .

これは私が1つのスレッドだけ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

もう1つの解決策は、変更可能性は必要ないものの、アイテムへの変更可能な参照をスレッドに移動することです。変更可能な参照は1つしかないので、コンパイラは別のスレッドで使用することが安全であることを認識しています。

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が' 'Send'を実装しているためこれが許可されています。 – bluss

関連する問題