我如何保证一个没有实现 Sync 的类型实际上可以在线程之间安全地共享?
How can I guarantee that a type that doesn't implement Sync can actually be safely shared between threads?
我有创建 RefCell
的代码,然后想将对该 RefCell
的引用传递给 单个 线程:
use crossbeam; // 0.7.3
use std::cell::RefCell;
fn main() {
let val = RefCell::new(1);
crossbeam::scope(|scope| {
scope.spawn(|_| *val.borrow());
})
.unwrap();
}
在完整的代码中,我使用了一个嵌入了 RefCell
的类型(typed_arena::Arena
). I'm using crossbeam 以确保线程不会超过它所使用的引用。
这会产生错误:
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
--> src/main.rs:8:15
|
8 | scope.spawn(|_| *val.borrow());
| ^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::RefCell<i32>`
= note: required because it appears within the type `[closure@src/main.rs:8:21: 8:38 val:&std::cell::RefCell<i32>]`
我相信我明白为什么会发生这个错误:RefCell
不是设计为从多个线程并发调用,并且由于它使用内部可变性,需要单个可变借用的正常机制不会阻止多个并发动作。这甚至记录在 Sync
:
Types that are not Sync
are those that have "interior mutability" in a non-thread-safe form, such as cell::Cell
and cell::RefCell
.
一切都很好,但是在这种情况下,我知道只有一个线程能够访问RefCell
。我如何向编译器确认我理解我在做什么并且我确保是这样的?当然,如果我认为这实际上是安全的推理是不正确的,我会很乐意被告知原因。
一种方法是使用带有 unsafe impl Sync
:
的包装器
use crossbeam; // 0.7.3
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());
})
.unwrap();
}
与 unsafe
一样,现在由您来保证内部 RefCell
确实不会同时从多个线程访问。据我了解,这应该足以避免引起数据竞争。
这种情况的另一个解决方案是将对项目的可变引用移动到线程中,即使不需要可变性。由于只能有一个可变引用,编译器知道在另一个线程中使用它是安全的。
use crossbeam; // 0.7.3
use std::cell::RefCell;
fn main() {
let mut val = RefCell::new(1);
let val2 = &mut val;
crossbeam::scope(|scope| {
scope.spawn(move |_| *val2.borrow());
})
.unwrap();
}
作为:
This is allowed because RefCell<i32>
implements Send
.
我有创建 RefCell
的代码,然后想将对该 RefCell
的引用传递给 单个 线程:
use crossbeam; // 0.7.3
use std::cell::RefCell;
fn main() {
let val = RefCell::new(1);
crossbeam::scope(|scope| {
scope.spawn(|_| *val.borrow());
})
.unwrap();
}
在完整的代码中,我使用了一个嵌入了 RefCell
的类型(typed_arena::Arena
). I'm using crossbeam 以确保线程不会超过它所使用的引用。
这会产生错误:
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
--> src/main.rs:8:15
|
8 | scope.spawn(|_| *val.borrow());
| ^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
= note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::RefCell<i32>`
= note: required because it appears within the type `[closure@src/main.rs:8:21: 8:38 val:&std::cell::RefCell<i32>]`
我相信我明白为什么会发生这个错误:RefCell
不是设计为从多个线程并发调用,并且由于它使用内部可变性,需要单个可变借用的正常机制不会阻止多个并发动作。这甚至记录在 Sync
:
Types that are not
Sync
are those that have "interior mutability" in a non-thread-safe form, such ascell::Cell
andcell::RefCell
.
一切都很好,但是在这种情况下,我知道只有一个线程能够访问RefCell
。我如何向编译器确认我理解我在做什么并且我确保是这样的?当然,如果我认为这实际上是安全的推理是不正确的,我会很乐意被告知原因。
一种方法是使用带有 unsafe impl Sync
:
use crossbeam; // 0.7.3
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());
})
.unwrap();
}
与 unsafe
一样,现在由您来保证内部 RefCell
确实不会同时从多个线程访问。据我了解,这应该足以避免引起数据竞争。
这种情况的另一个解决方案是将对项目的可变引用移动到线程中,即使不需要可变性。由于只能有一个可变引用,编译器知道在另一个线程中使用它是安全的。
use crossbeam; // 0.7.3
use std::cell::RefCell;
fn main() {
let mut val = RefCell::new(1);
let val2 = &mut val;
crossbeam::scope(|scope| {
scope.spawn(move |_| *val2.borrow());
})
.unwrap();
}
作为
This is allowed because
RefCell<i32>
implementsSend
.