[RESOLVED] Working with mutiple threads and pass variables


#1

Sorry to ask this question here but i dont know what to do anymore !
In MY CODE i’m trying to parallize a search for the maximum element in a array/vector, but i dont think i’m undertanding how threads work in RUST.:disappointed_relieved:
In the code, i’m basicallly trying to allow the vector to be accessed by any thread (just to read), and find que maximum value in that interval. After each thread find the maximum value, they should write to a maximum variable (in coordinated order, which i dont know how to do) or each thread store your maximum in a vector to finally the main thread look for the maximum in this vector.

My problem is that this code is not working at all, i came from C language, and this is totally diffente to what i’m used to do.

I wish you guys cloud comment in my code, or just show link to implementations that can clarify things to me. Once again, HERE IS THE LINK to the code !

Thanks a lot!


#2

Threads in Rust can return values, which you can use to your advantage. Here is a sample playground with your code doing just that.

Note that you need to put the Vec into an Arc so that the compiler is satisfied that the Vec stays alive while the background threads are running.


#3

I understand that this is a learning exercise, and hope that @vitalyd’s answer will help you get past the current roadblock. But once you are more familiar with Rust, I would like to suggest getting acquainted with two libraries which will make the task of parallelizing Rust code much easier for you:

Rayon provides a parallel version of the Rust iterator mechanism, where work is automatically offloaded to a thread pool instead of being executed sequentially. Using it, your example becomes a marvel of conciseness. In general, you can apply this library to any kind of “embarassingly parallel” problem, it is trivial to use with std containers and not very difficult to adapt to your own data structures.

For cases where you still want to control thread spawning manually, crossbeam provides a scoped thread abstraction, which guarantees that threads will be joined before their host “scope” is existed. The Rust borrow checker can then leverage this knowledge to assert that the shared data does not need to live beyond the end of the scope, which in practice means that you will need to put data which is shared between threads in Arcs much less often.


#4

@HadrienG, thaks for you time to answer my question, i really appreciate it.


#5

@vitalyd, thaks very much for your help ! really appreciate it !


#6

The most important things have been already said by @HadrienG and @vitalyd, therefore I imagine that you solved your real-case problem.

Nevertheless, this maybe can be useful for some better understanding.

extern crate crossbeam;

static N_THREADS: u32 = 4;
static ELEMENTS: u32 = 99999999;

fn main() {
    // `vector` can be created wihout being mutable
    let vector: Vec<_> = (0u32..ELEMENTS).collect();

    // thanks to crossbeam, the threads created with `scope.spawn` live until the end of
    // `crossbeam::scope`, and the references to `vector` are possible
    let max_value = crossbeam::scope(|scope| {
        let chunk_size = if ELEMENTS % N_THREADS == 0 {
            ELEMENTS / N_THREADS
        } else {
            ELEMENTS / N_THREADS + 1
        } as usize;

        // same for scoped threads, with the help of chunks
        let spawn_threads: Vec<_> = vector
            // Divide the vector in chunks -- vector is implicitly casted to a slice, which can be
            // divided in multiple sub-slices
            .chunks(chunk_size)
            .map(|chunk| {
                // map every chunk to a thread handler, which searches the max value
                scope.spawn(move || {
                    // Let's use the available functions!
                    // Note: `max` returns an option, which could be just wrapped if we can be sure
                    // that no chunk is empty. In this case, it is better to return the `Option`
                    chunk.iter().max()
                })
            })
            .collect();

        spawn_threads
            .into_iter()
            // Map every thread to its result, once joined
            .map(|thread| thread.join())
            // Take only the `Option` that are not `None`
            .filter_map(|x| x)
            // Find the max value
            .max()
            // We can unwrap as soon ELEMENTS != 0
            .unwrap()
    });

    println!("Max value: {}", max_value);
}

You have to know that my code is bad, mainly because there is a big overhead for the spawning of the threads. But my idea is that you can use some features of the stdlib to have more expressive code (think about how the use of chunk helps a lot to understand what the code is doing).

This can be useful to learn a different approach, if you want performance go with rayon as said by @HadrienG :wink:


#7

@dodomorandi, to be honest, i don’t understand your code because i’m really new in Rust. But i thank you for your time, and i’m gonna try to learn more to get this code ASAP.