Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code not running in parallel when using thread::scoped

Tags:

rust

Can someone please explain why the code below does not run in parallel? I guess I don't understand how thread::scoped works..

use std::thread;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::old_io::timer;

fn main() {
    let buf = Arc::new(Mutex::new(Vec::<String>::new()));
    let res = test(buf);
    println!("{:?}", *res.lock().unwrap());
}

fn test(buf: Arc<Mutex<Vec<String>>>) -> Arc<Mutex<Vec<String>>> {
    let guards: Vec<_> = (0..3).map( |i| {
        let mtx = buf.clone();
        thread::scoped(|| {
            println!("Thread: {}", i);
            let mut res = mtx.lock().unwrap();
            timer::sleep(Duration::seconds(5));
            res.push(format!("thread {}", i));
        });
    }).collect();
    buf
}

The code is based on the examples here where it's stated:

The scoped function takes one argument, a closure, indicated by the double bars ||. This closure is executed in a new thread created by scoped. The method is called scoped because it returns a 'join guard', which will automatically join the child thread when it goes out of scope. Because we collect these guards into a Vec, and that vector goes out of scope at the end of our program, our program will wait for every thread to finish before finishing.

Thanks

like image 240
mhristache Avatar asked Mar 08 '15 17:03

mhristache


1 Answers

This is a tricky case. The problem is the humble semicolon. Look at this minimized code:

thread::scoped(|| {});

That semicolon means that the result of the collect isn't a vector of JoinGuards — it's a Vec<()>! Each JoinGuard is dropped immediately, forcing the thread to finish before the next iteration starts.

When you fix this issue, you'll hit the next problem, which is that i and mtx don't live long enough. You'll need to move them into the closure:

thread::scoped(move || {})
like image 171
Shepmaster Avatar answered Sep 18 '22 03:09

Shepmaster