如何跨线程共享引用?

ust*_*ion 4 multithreading rust

我无法在线程之间共享引用.

trait Facade { /*some functions*/ }

struct Client<'a> {
    facade: &'a mut Facade,
    join_grd: thread::JoinGuard<'a()>,
}

impl<'a> Client<'a> {
    pub fn new(my_facade: &'a mut Facade) -> Client<'a> {
        Client {
            facade: my_facade,
        join_grd: thread::scoped(|| Client::start(my_facade)),
        }
    }

    fn start(my_facade: &'a mut Facade) { unimplemented!() }
}
Run Code Online (Sandbox Code Playgroud)

鉴于我在Rust中的新手状态,我对概念和错误感到困惑.我如何实现上述目标?

Vla*_*eev 8

由于Rust中的可变别名保证,我很确定你不能这样做.在Rust中,你不能同时对同一个东西进行两次可变引用,但这正是你的代码中发生的事情:你存储my_facade到字段中Client,同时你试图将它传递给start()另一个中的方法线.这将需要对Facade不允许的两个可变引用.

编译器在您的代码上发出的​​实际错误是由于您使用的是非移动闭包.如果您将thread::scoped()实例化更改为:

join_grd: thread::scoped(move || Client::start(my_facade))
Run Code Online (Sandbox Code Playgroud)

错误会更明智:

test.rs:16:60: 16:69 error: cannot move `my_facade` into closure because it is borrowed
test.rs:16             join_grd: thread::scoped(move || Client::start(my_facade))
                                                                      ^~~~~~~~~
test.rs:15:21: 15:30 note: borrow of `*my_facade` occurs here
test.rs:15             facade: my_facade,
                               ^~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

这实际上意味着由于&mut引用是唯一的并且被移动而不是复制,因此您无法复制它们.类似的代码与常规&引用而不是&mut(以及一个额外的Sync父特征Facade)工作正常.

您必须重新考虑您的体系结构以修复此错误.单独从这段代码中很难理解你想要什么,所以我不能给出任何确切的建议,但你可以考虑使用Arc,Mutex如果你想在线程之间共享可变状态.


天真的用法Arc/Mutex如下:

fn start(my_facade: Arc<Mutex<Facade>>)
Run Code Online (Sandbox Code Playgroud)

不会起作用,因为它Facade是一种特质,而不是常规类型.当您使用traits作为类型时,您实际上选择以trait对象的形式进行动态调度.简而言之,特征对象不能直接使用; 他们应该总是在指针后面.您的原始程序也使用了特征对象(&'a mut Facade是一个特征对象).理想情况下,我们应该能够使用任何类型的智能指针形成特征对象,理想情况下Arc<Mutex<Facade>>应该可以工作,但遗憾的是现在特征对象只能用&,&mut或者Box:

fn start(my_facade: Arc<Mutex<Box<Facade>>>)
Run Code Online (Sandbox Code Playgroud)

这就是Sized你观察到的错误的原因.

但是,您还应该考虑不使用trait对象,只使用泛型:

trait Facade: Send { fn f(&self); }

struct Client<'a, F: Facade> {  // '
    facade: Arc<Mutex<F>>,
    join_grd: thread::JoinGuard<'a, ()>,  // '
}

impl<'a, F: Facade+'a> Client<'a, F> {  // '
    pub fn new(my_facade: Arc<Mutex<F>>) -> Client<'a, F> {  // '
        let my_facade_2 = my_facade.clone();  // clone the Arc pointer
        Client {
            facade: my_facade,
            join_grd: thread::scoped(move || Client::start(my_facade_2)),
        }
    }

    fn start(my_facade: Arc<Mutex<F>>) { unimplemented!() }
}
Run Code Online (Sandbox Code Playgroud)

你还需要添加Send结合要么在性状本身(如在上面的例子中)或在F类型变量(如在F: Facade+Send+'a),因为只有Send数据可以线程之间传输安全,所以需要指定F是Send,无论是直接或作为supertrait约束Facade.