Wait for threads


#1

Hi!
I’m trying to write a program which will use threads to multiply 2 matrices. It should create a thread for each core, so the number of threads can be different. The problem is how to wait for all threads if i don’t know how much of them will be created? I’ve tried to make a vec[0;threads], then used mutex to give one element of that vec to a thread, then used smth like vec[i] = thread::spawn…, and checked that vector in the main by .join().
But that didn’t work, don’t know why.
Also tried to make each thread set some variable as 1 at the end and after that wait in main for all them to be equal 1, but this doesn’t work also, the cycle just doesn’t stop.
What can you advise? Thanks!
ps bad english, sorry :slight_smile:


#2

Solved.
The problem was while() cycle.

                    for q in 0..threads {
                    loop {
                    thread::sleep_ms(5);
                    let f_clone = f.clone();
                    let mut fshared = f_clone.lock().unwrap();
                    if (fshared[q]==0) {break;}
                    }
                    }

So i used vec! + mutex + sleep
Maybe will help someone


#3

You should be able to add the JoinHandle for each spawned thread to a Vec and then loop over them and join() each one. Did you try that? It’s not very clear what didn’t work without seeing the code. I see you solved it with some combination of sleeping, but that shouldn’t be needed.


#4

Yep, that was my first idea, but in this case it turned out that types do not match. Thus i thought it will not be easy to solve such a mismatch as "i want integer here but you give me vec<mutex< lots of other creepy stuff >> (but i tried!) and just did sleep version.


#5

Could you paste the code here or better yet, set up a playground link? I’m curious what the type mismatch is exactly.


#6

Would something like rayon help if all you want to do is spread the load across multiple cores?


#7

OK, i’ll try to reestablish that version and paste it here. I’m sure i’ve just made mistake there and that’s the reason why it didn’t work, but for me it was easier to do sleep instead.
Michael, i’ve already done this task using crossbeam, i was interested to know how can i do it myself
ps i’m new to Rust so, you know, my code could be not very clear :slight_smile:


#8

Well, thats how i tried to do this with .join().


and compiler errors:
error[E0308]: mismatched types
–> src/main.rs:104:13
|
104 | fshared[q]=thread::spawn(move ||{
| __________^
105 | | let mut shared = z_clone.lock().unwrap();
106 | | for z in (w_clone[2q])…(w_clone[(2q)+1]) {
107 | | for i in 0…d{
… |
114 | |
115 | | });
| |
^ expected integral variable, found struct std::thread::JoinHandle
|
= note: expected type {integer}
found type std::thread::JoinHandle<()>

error[E0599]: no method named join found for type {integer} in the current scope
–> src/main.rs:132:28
|
132 | let mut wait = fshared[q].join();
| ^^^^

error: aborting due to 2 previous errors


And thats how it looks now:


#9

The type issue is because of this line:

let mut flags = vec![0;threads];

That creates a Vec holding integers, and just zeros it out. You want a Vec that holds JoinHandle values. So you can replace that line with:

let mut flags = Vec::with_capacity(threads);
// or just vec![];

The other issue is you don’t need to wrap this Vec in a Arc<Mutex<...>> - this Vec is not shared with the worker threads - you just add a JoinHandle representing those threads to it, but that all happens on the main thread. So your join loop will simply be:

for handle in flags {
    let _ = handle.join(); // maybe consider handling errors propagated from the thread here
}

#10

Looks good, but i can’t make it work with “for”. Even this doesn’t work: (i’ve commented out the rest of progam)
let mut flags = Vec::with_capacity(threads);
for i in 0…threads {
flags[i]=1;
}
Err: thread ‘main’ panicked at 'index out of bounds: the len is 0 but the index is 0’
So i guess the capacity of vec is 0, somehow.


#11

Oh yeah, I forgot to mention that - use Vec::push to add the JoinHandle instances to the vec instead of indexing.


#12

Thanks, now it works.
But i don’t understand why this works:
for handle in flags { let _ = handle.join(); }
As i understand (also i see i’m wrong) this is equal to this
for i in 0..flags { let _ = flags[i].join(); }
How does it work? I thought we do just this: for (any name for var) in (just numbers) {some work;}


#13

Notice that the vec owns the JoinHandles here. When you index into the vec, you get either an immutable or mutable borrow, depending on context/usage. JoinHandle::join takes self as a parameter, meaning it consumes (moves) the handle. To consume/move it, we need to own it.

Iterating over a vec by value, that is for item in vec rather than for item in &vec (or &mut vec), consumes the vec but you gain ownership over its elements in the process. Once you have ownership of the handle, you can join() and consume it.