Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
924 views
in Technique[技术] by (71.8m points)

multithreading - 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. How do I achieve the above ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm pretty sure you can't do this due to the mutable aliasing guarantees in 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. This would require having two mutable references to the same Facade which is disallowed.

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:

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. Similar code with the regular & reference instead of &mut (and an additional Sync parent trait on Facade) works fine.

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.


Naive usage of Arc/Mutex like this:

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

won't work because Facade is a trait, not a regular type. When you use traits as types, you're in fact opting into dynamic dispatch in form of trait objects. 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). 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:

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

This is the reason of the error about Sized that you observe.

However, you should also consider not using trait objects at all and just use generics:

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...