简体   繁体   中英

Copying a struct for use on another thread

I have a struct:

struct MyData {
    x: i32
}

I want to asynchronously start a long operation on this struct.

My first attempt was this:

fn foo(&self) { //should return immediately
    std::thread::Thread::spawn(move || { 
        println!("{:?}",self.x); //consider a very long operation
    });
}

Clearly the compiler cannot infer an appropriate lifetime due to conflicting requirements because self may be on the stack frame and thus cannot be guaranteed to exist by the time the operation is running on a different stack frame.

To solve this, I attempted to make a copy of self and provide that copy to the new thread:

fn foo(&self) { //should return immediately
    let clone = self.clone();
    std::thread::Thread::spawn(move || { 
        println!("{:?}",clone.x); //consider a very long operation
    });
}

I think that does not compile because now clone is on the stack frame which is similar to before. I also tried to do the clone inside the thread, and that does not compile either, I think for similar reasons.

Then I decided maybe I could use a channel to push the copied data into the thread, on the theory that perhaps channel can magically move (copy?) stack-allocated data between threads, which is suggested by this example in the documentation . However the compiler cannot infer a lifetime for this either:

fn foo(&self) { //should return immediately
    let (tx, rx) = std::sync::mpsc::channel();
    tx.send(self.clone());
    std::thread::Thread::spawn(move || { 
        println!("{:?}",rx.recv().unwrap().x); //consider a very long operation
    });
}

Finally, I decided to just copy my struct onto the heap explicitly, and pass an Arc into the thread. But not even here can the compiler figure out a lifetime:

fn foo(&self) { //should return immediately
    let arc = std::sync::Arc::new(self.clone());
    std::thread::Thread::spawn(move || { 
        println!("{:?}",arc.clone().x); //consider a very long operation
    });
}

Okay borrow checker, I give up. How do I get a copy of self onto my new thread?

I think your issue is simply because your structure does not derive the Clone trait. You can get your second example to compile and run by adding a #[derive(Clone)] before your struct's definition.

What I don't understand in the compiler behaviour here is what .clone() function it tried to use here. Your structure indeed did not implement the Clone trait so should not by default have a .clone() function.

playpen

You may also want to consider in your function taking self by value , and let your caller decide whether it should make a clone, or just a move.

As an alternative solution, you could use thread::scoped and maintain a handle to the thread. This allows the thread to hold a reference, without the need to copy it in:

#![feature(old_io,std_misc)]

use std::thread::{self,JoinGuard};
use std::old_io::timer;
use std::time::duration::Duration;

struct MyData {
    x: i32,
}

// returns immediately
impl MyData {
    fn foo(&self) -> JoinGuard<()> { 
        thread::scoped(move || { 
            timer::sleep(Duration::milliseconds(300));
            println!("{:?}", self.x); //consider a very long operation
            timer::sleep(Duration::milliseconds(300));
        })
    }
}

fn main() {
    let d = MyData { x: 42 };
    let _thread = d.foo();
    println!("I'm so fast!");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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