美文网首页Rust与区块链编程
为何thread::spawn中支持Arc而不支持Rc?

为何thread::spawn中支持Arc而不支持Rc?

作者: 红叔笔记 | 来源:发表于2020-06-07 16:56 被阅读0次

    看这段代码:

    fn main() {
        let counter = Rc::new(Mutex::new(0));
        let mut handles = vec![];
    
        for _ in 0..10 {
            let counter = Rc::clone(&counter);
            let handle = thread::spawn(move || {
                let mut num = counter.lock().unwrap();
    
                *num += 1;
            });
            handles.push(handle);
        }
    
        for handle in handles {
            handle.join().unwrap();
        }
    
        println!("Result: {}", *counter.lock().unwrap());
    }
    
    

    编译器报错如下:

    error[E0277]: `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
       --> src/main.rs:11:22
        |
    11  |           let handle = thread::spawn(move || {
        |  ______________________^^^^^^^^^^^^^_-
        | |                      |
        | |                      `std::rc::Rc<std::sync::Mutex<i32>>` cannot be sent between threads safely
    12  | |             let mut num = counter.lock().unwrap();
    13  | |
    14  | |             *num += 1;
    15  | |         });
        | |_________- within this `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
        |
        = help: within `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<std::sync::Mutex<i32>>`
        = note: required because it appears within the type `[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc<std::sync::Mutex<i32>>]`
    
    

    有人说, 很简单, 因为Arc就是为了解决Rc的问题诞生的, 这个逻辑简直是毫无逻辑, 很简单, 编译器为什么报错呢? 编译器比人简单多了, 而且它的逻辑还很容易理解。

    我的理解:

    首先看下spawn的签名:

    pub fn spawn<F, T>(f: F) -> JoinHandle<T>
    where
        F: FnOnce() -> T,
        F: Send + 'static,
        T: Send + 'static,
    {
        Builder::new().spawn(f).expect("failed to spawn thread")
    }
    

    这里很明确的要求是, spwan的参数是一个closure, 并且该closure捕获的变量要求实现Send。

    我认为编译器报错的逻辑核心就是在这里, Rc里没有实现Send, 而Arc实现了逻辑。

    看下Arc的源码:

    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
    

    所以, Arc符合签名要求, 而Rc不符合要求。

    结合前面的channel的例子, 因为Sender实现了Send, 源码如下:

    #[stable(feature = "rust1", since = "1.0.0")]
    unsafe impl<T: Send> Send for Sender<T> {}
    
    #[stable(feature = "rust1", since = "1.0.0")]
    impl<T> !Sync for Sender<T> {}
    

    小结: 目前看 spawn的方法签名默认就要求实现Send, 没有实现Send的自然就报错, Rc的报错原因即是如此。

    所以, 说啥Arc是为了解决Rc的问题, 本质还是Arc实现了Send。

    当然, 上面的closure 里的签名其实我还是不太理解的一个地方就是:

    pub fn spawn<F, T>(f: F) -> JoinHandle<T>
    where
        F: FnOnce() -> T,
        F: Send + 'static,
        T: Send + 'static,
    

    这里对F的要求是实现Send, 我一直理解是某个变量类型实现了Send, 但是一个closure实现Send这个trait是什么意思? 目前只能勉强理解为捕获的变量实现Send 这个trait。

    欢迎反馈。

    更新: 来自群友北纬27度的解释:

    可以这么理解,因为closure本质上是一个匿名结构体,一个结构体要实现Send的前提是他的所有成员都实现了Send,而这个匿名结构体的成员就是他捕获的变量,所以那些变量也要实现Send才行

    相关文章

      网友评论

        本文标题:为何thread::spawn中支持Arc而不支持Rc?

        本文链接:https://www.haomeiwen.com/subject/ymbctktx.html