简体   繁体   English

如何跨线程共享引用?

[英]How can I share references across threads?

I am unable to share a reference between threads. 我无法在线程之间共享引用。

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!() }
}

Given my newbie status in Rust, I'm getting confused with concepts and errors. 鉴于我在Rust中的新手状态,我对概念和错误感到困惑。 How do I achieve the above ? 我如何实现上述目标?

I'm pretty sure you can't do this due to the mutable aliasing guarantees in Rust. 由于Rust中的可变别名保证,我很确定你不能这样做。 In Rust you can't have two mutable references to the same thing at the same time, but this is exactly what happens in your code: you store my_facade to the field of Client and at the same time you are trying to pass it to start() method in another thread. 在Rust中,你不能同时对同一个东西进行两次可变引用,但这正是你的代码中发生的事情:你将my_facade存储到Client的字段中,同时你试图将它传递给start()另一个线程中的start()方法。 This would require having two mutable references to the same Facade which is disallowed. 这将需要对同一Facade两个可变引用,这是不允许的。

The actual errors which compiler emits on your code are caused by that you're using a non-moving closure. 编译器在您的代码上发出的​​实际错误是由于您使用的是非移动闭包。 If you change thread::scoped() instantiation to this: 如果将thread::scoped()实例化更改为:

join_grd: thread::scoped(move || Client::start(my_facade))

the error would be more sensible: 错误会更明智:

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,
                               ^~~~~~~~~

This essentially means that since &mut references are unique and are moved instead of copied, you can't duplicate them. 这实际上意味着,由于&mut引用是唯一的并且被移动而不是复制,因此您无法复制它们。 Similar code with the regular & reference instead of &mut (and an additional Sync parent trait on Facade ) works fine. 类似的代码与常规&引用而不是&mut (和Facade上的另一个Sync父特征)工作正常。

You have to rethink your architecture to fix this error. 您必须重新考虑您的体系结构以修复此错误。 It is difficult to understand what you want from this piece of code alone, so I can't give any exact advices, but you may consider using Arc and Mutex if you want to share mutable state between threads. 很难单独从这段代码中理解你想要什么,所以我不能给出任何确切的建议,但如果你想在线程之间共享可变状态,你可以考虑使用ArcMutex


Naive usage of Arc/Mutex like this: 像这样天真地使用Arc/Mutex

fn start(my_facade: Arc<Mutex<Facade>>)

won't work because Facade is a trait, not a regular type. 将无法正常工作,因为Facade是一种特质,而不是常规类型。 When you use traits as types, you're in fact opting into dynamic dispatch in form of trait objects. 当您使用traits作为类型时,您实际上选择以trait对象的形式进行动态调度 In short, trait objects can't be used directly; 简而言之,特征对象不能直接使用; they should always be behind a pointer. 他们应该总是在指针后面。 Your original program also used trait objects ( &'a mut Facade is a trait object). 你的原始程序也使用了trait对象( &'a mut Facade是一个特征对象)。 Ideally we should be able to form trait objects with any kind of smart pointer, and ideally Arc<Mutex<Facade>> should work, but unfortunately for now trait objects can only be created with & , &mut or Box : 理想情况下,我们应该能够使用任何类型的智能指针形成特征对象,理想情况下Arc<Mutex<Facade>>应该可以工作,但不幸的是现在特征对象只能用&&mutBox

fn start(my_facade: Arc<Mutex<Box<Facade>>>)

This is the reason of the error about Sized that you observe. 这就是你观察到的关于Sized错误的原因。

However, you should also consider not using trait objects at all and just use generics: 但是,您还应该考虑不使用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!() }
}

You also need to add Send bound either on the trait itself (as in the example above) or on F type variable (as in F: Facade+Send+'a ) because only Send data may be transferred between threads safely, so you need to specify that F is Send , either directly or as a supertrait constraint on Facade . 您还需要在特征本身(如上例中所示)或F类型变量(如F: Facade+Send+'a )中添加Send bound,因为只有Send数据可以安全地在线程之间传输,因此您需要指定FSend ,直接或作为Facade的超级约束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM