代码之家  ›  专栏  ›  技术社区  ›  skyde

Rayon如何防止在线程之间使用refcell<t>、cell<t>和rc<t>?

  •  1
  • skyde  · 技术社区  · 6 年前

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

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

    我明白 core::marker::Sync 标记可以在线程之间安全共享的类型,但我不理解rayon类型声明和编译器是如何强制执行的!

    1 回复  |  直到 6 年前
        1
  •  3
  •   Sven Marnach    6 年前

    实际上,您自己回答了您的问题,所有需要在线程之间共享的闭包都需要 Sync Rayon的API只要求 同步 通过特性界限。例如,请参见 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以一种需要它的方式使用一个闭包时, 同步 例如,通过将其传递给较低级别的API,Rayon使用 同步 性状结合。这反过来又确保了闭包中存储的所有内容 同步 ,所以你不能储存任何 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() 它仍然向您指出相关的方法,并解释它期望闭包是 同步 以及为什么没有。

    推荐文章