What is the proper way to manage a dynamic size thread pool?

Thanks to buddies in #57126, I can finally setup a basic parallel thread model in rust.

On this basis I would like to propose some further requirements. In the previous topic, I wanted to run three threads in parallel to compute the mean, median and standard deviation of an Vec<f64>. Now let's assume a more practical situation, suppose that we have a dual-core CPU, and four task to be computed (assume that they're mean/median/stddev/variance), I want to manage a simple thread pool to achieve the fastest execution time.

Spontaneously, we want to keep two worker threads running at a time(each runs on a actual CPU core), and we got a main thread as master, use std::sync::mpsc to make worker run and get return vales.

I tried to implement this logic from the following code:

fn main() {
    let mut threads:Vec<thread::JoinHandle<f64>> = Vec::new();
    // open a thread pool
    for index in 0..num {
        threads.push(
            thread::spawn(move || {
                100.0
            })
        )
    }
    // do some stuff

    // close the pool
    for index in 0..num {
        threads[index].join();
    }
}

However I got a error message:

error[E0507]: cannot move out of index of `Vec<JoinHandle<f64>>`
  --> src\main.rs:16:9
   |
16 |         threads[index].join();
   |         ^^^^^^^^^^^^^^ move occurs because value has type `JoinHandle<f64>`, which does not implement the `Copy` trait       

What should I do?

The join method consumes the JoinHandle, so you must iterate in a way that gives you ownership. This destroys the vector in the process.

for thread in threads {
    thread.join();
}
2 Likes

The reason why it isn't working currently is that indexing a vector is not allowed to take ownership of the items, because taking out items at random would create holes in the vector but vectors are required to be fully intact. And the reason JoinHandle::join takes ownership is so you can't get the value returned by a thread more than once.

The correct solution is as Alice mentioned: the IntoIterator implementation of Vec (accessed by a for item in vec loop) allows you to consume the Vec and take ownership of all its items. Unlike indexing it can only be called once, which gives it this ability.

1 Like