人造丝如何阻止线程之间使用RefCell <t>,Cell <t>和Rc <t>?

时间:2018-12-01 21:23:06

标签: rust thread-safety rayon interior-mutability

Rayon文档说,它保证使用Rayon API不会引入数据争用。

编译器如何知道闭包调用的方法未共享可变状态,例如RefCell<T>Cell<T>,或者使用非线程安全的结构(例如{{1 }}?

我知道Rc<T>标记了可以在线程之间安全共享的类型,但是我不了解Rayon类型声明和编译器是如何执行的!

1 个答案:

答案 0 :(得分:3)

您实际上回答了自己的问题–所有需要在线程之间共享的闭包都必须为Sync,而Rayon的API只是要求它们通过特征界线为Sync。例如,请参见documentation of ParallelIterator::map(),该方法将方法指定为

fn map<F, R>(self, map_op: F) -> Map<Self, F> where
    F: Fn(Self::Item) -> R + Sync + Send,
    R: Send, 

这里没有更深层次的魔术了-每当Rayon以要求Sync的方式使用闭包时,例如通过将其传递给较低级别​​的API,Rayon限制了具有Sync特征限制的相应参数类型。反过来,这确保了闭包中存储的所有内容都是Sync,因此您不能在闭包中存储任何RefCell

在这种情况下,您也可以要求编译器提供解释。例如,如果您尝试编译此代码

use std::cell::RefCell;
use rayon::prelude::*;

fn main() {
    let c = RefCell::new(5);
    let _ = [1, 2, 3]
        .par_iter()
        .map(|i| i * *c.borrow())
        .sum();
}

您会收到此错误(playground

error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
  --> src/main.rs:10:10
   |
10 |         .map(|i| i * *c.borrow())
   |          ^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
   |
   = help: within `[closure@src/main.rs:10:14: 10:33 c:&std::cell::RefCell<i32>]`, the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
   = note: required because it appears within the type `&std::cell::RefCell<i32>`
   = note: required because it appears within the type `[closure@src/main.rs:10:14: 10:33 c:&std::cell::RefCell<i32>]`

尽管不幸的是,编译器没有直接提及map()的参数的特征绑定,但仍将您指向相关方法,并解释说它期望闭包为Sync,并且不是的原因。