mhristache
mhristache

Reputation: 2188

Code not running in parallel when using thread::scoped

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

Upvotes: 6

Views: 518

Answers (1)

Shepmaster
Shepmaster

Reputation: 432199

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 || {})

Upvotes: 11

Related Questions