Using thread::spawn without keyword move

Here is my code:


fn calc(x: Vec<i32>, y: i32) -> Vec<i32> {
    //some block take long time to calculate
    x.iter().map(|x| x + y).collect()
}

let a = vec![1i32, 2, 3];//some very large vector

let res1 = spawn(move || calc(a.clone(), 1))
let res2 = spawn(move || calc(a.clone(), 2))
let res3 = spawn(move || calc(a.clone(), 3))

So, every time I start a spawn I have to clone a. a is a very large Vec, cloning it is expensive.
Is there a way I can use spawn withing using refrence? Like this:

fn calc(x: &Vec<i32>, y: i32) -> Vec<i32> {
    //some block take long time to calculate
    x.iter().map(|x| x + y).collect()
}

let a = vec![1i32, 2, 3];//some very large vector

let res1 = spawn(|| calc(&a, 1))
let res2 = spawn(|| calc(&a, 2))
let res3 = spawn(|| calc(&a, 3))

Your function is defined to take ownership of the Vec so you will always need to clone to call it multiple times for the same Vec. You don't appear to need ownership though, so you can change calc to take a reference[1]

You can use std::thread::scope on recent rust versions to spawn threads that can borrow from the current thread. The catch is that the threads are all joined[2] before the call to scope returns, which may not be what you want[3].

You can also imitate scope with an Arc if you're on an older version of rust[4]

Playground

use std::{sync::Arc, thread};

fn calc(x: &[i32], y: i32) -> Vec<i32> {
    //some block take long time to calculate
    x.iter().map(|x| x + y).collect()
}

fn scoped() {
    let a = vec![1i32, 2, 3]; //some very large vector

    thread::scope(|scope| {
        scope.spawn(|| calc(&a, 1));
        scope.spawn(|| calc(&a, 2));
        scope.spawn(|| calc(&a, 3));
    });
}

fn shared() {
    let a = Arc::new(vec![1i32, 2, 3]); //some very large vector

    thread::spawn({
        let a = a.clone();
        move || calc(&a, 1)
    })
    .join()
    .unwrap();
    thread::spawn({
        let a = a.clone();
        move || calc(&a, 2)
    })
    .join()
    .unwrap();
    thread::spawn(move || calc(&a, 3)).join().unwrap();
}

  1. generally &[i32] is preferred over &Vec<i32> since it's more flexible ↩︎

  2. meaning the current thread waits for the other thread to finish before continuing ↩︎

  3. all of the threads are spawned before any are joined though ↩︎

  4. The simple version included below doesn't exactly match scope's behavior since it joins each thread immediately ↩︎

4 Likes

Here's a version returning the results.

2 Likes