Executing multiple threads at a time

Hi most valuable brothers. There is a question in my mind which disturbing me. How the for loop starts multiple threads at a time? All threads needs to join().unwrap() for able to start but how the for loop continues to another thread although the previous thread didn't finish yet. Let me example code:

fn main() -> anyhow::Result<()> {
    let mut threads: Vec<JoinHandle<()>> = vec![];

    for thread_no in 0..10 {
        threads.push(thread::spawn(move || loop {
            for counter in 1..10 {
                println!("thread_no: {thread_no}, counter: {counter}");
                let wait_time_millis = rand::thread_rng().gen_range(100..555);

                thread::sleep(Duration::from_millis(wait_time_millis));
            }

            println!("thread_no: {thread_no}, counters done.");
            thread::sleep(Duration::from_millis(1000));
        }));
    }

    for (index, thread) in threads.into_iter().enumerate() {
        println!("Starting that thread: {index}");

        thread.join().map_err(|_| anyhow!("Thread error."))?;
    }
    Ok(())
}

Result:

thread_no: 0, counter: 1
thread_no: 1, counter: 1
thread_no: 2, counter: 1
thread_no: 3, counter: 1
thread_no: 6, counter: 1
thread_no: 4, counter: 1
thread_no: 5, counter: 1
thread_no: 7, counter: 1
Starting that thread: 0
thread_no: 8, counter: 1
thread_no: 9, counter: 1
thread_no: 0, counter: 2
thread_no: 9, counter: 2
thread_no: 8, counter: 2
thread_no: 6, counter: 2
thread_no: 4, counter: 2
thread_no: 3, counter: 2
thread_no: 1, counter: 2
thread_no: 0, counter: 3
thread_no: 6, counter: 3
thread_no: 7, counter: 2
thread_no: 2, counter: 2
thread_no: 0, counter: 4
thread_no: 5, counter: 2
thread_no: 4, counter: 3
thread_no: 8, counter: 3
thread_no: 1, counter: 3
thread_no: 6, counter: 4
thread_no: 9, counter: 3
thread_no: 3, counter: 3
thread_no: 7, counter: 3
thread_no: 3, counter: 4
thread_no: 1, counter: 4
thread_no: 0, counter: 5
thread_no: 2, counter: 3
thread_no: 5, counter: 3
thread_no: 7, counter: 4
thread_no: 6, counter: 5
thread_no: 9, counter: 4
thread_no: 4, counter: 4
thread_no: 2, counter: 4
thread_no: 8, counter: 4
thread_no: 0, counter: 6
thread_no: 5, counter: 4
thread_no: 1, counter: 5
thread_no: 3, counter: 5
thread_no: 9, counter: 5
thread_no: 2, counter: 5
thread_no: 1, counter: 6
thread_no: 8, counter: 5
thread_no: 9, counter: 6
thread_no: 7, counter: 5
thread_no: 4, counter: 5
thread_no: 6, counter: 6
thread_no: 5, counter: 5
thread_no: 0, counter: 7
thread_no: 8, counter: 6
thread_no: 3, counter: 6
thread_no: 9, counter: 7
thread_no: 0, counter: 8
thread_no: 2, counter: 6
thread_no: 6, counter: 7
thread_no: 1, counter: 7
thread_no: 4, counter: 6
thread_no: 7, counter: 6
thread_no: 2, counter: 7
thread_no: 5, counter: 6
thread_no: 0, counter: 9
thread_no: 6, counter: 8
thread_no: 8, counter: 7
thread_no: 9, counter: 8
thread_no: 3, counter: 7
thread_no: 1, counter: 8
thread_no: 6, counter: 9
thread_no: 4, counter: 7
thread_no: 9, counter: 9
thread_no: 2, counter: 8
thread_no: 7, counter: 7
thread_no: 8, counter: 8
thread_no: 1, counter: 9
thread_no: 0, counters done.
thread_no: 2, counter: 9
thread_no: 7, counter: 8
thread_no: 5, counter: 7
thread_no: 3, counter: 8
thread_no: 9, counters done.
thread_no: 4, counter: 8
thread_no: 8, counter: 9
thread_no: 6, counters done.
thread_no: 3, counter: 9
thread_no: 4, counter: 9
thread_no: 1, counters done.
thread_no: 2, counters done.
thread_no: 7, counter: 9
thread_no: 5, counter: 8
thread_no: 3, counters done.
thread_no: 4, counters done.
thread_no: 8, counters done.
thread_no: 5, counter: 9
thread_no: 7, counters done.
thread_no: 0, counter: 1
thread_no: 9, counter: 1
thread_no: 6, counter: 1
thread_no: 9, counter: 2
thread_no: 5, counters done.
thread_no: 1, counter: 1
thread_no: 2, counter: 1
thread_no: 0, counter: 2
thread_no: 6, counter: 2
thread_no: 0, counter: 3
thread_no: 3, counter: 1

As you can see all threads have endless loop and they have their own number and printing it. How is it possible? And I want to draw your attention to that there is that output only one time: Starting that thread: 0 If all threads starting then the next print! must work but it doesn't working. Why?

Thanks for your replies.

As you can see all threads have endless loop and they have their own number and printing it.

You have a infinite loop here: threads.push(thread::spawn(move || loop { because of loop. This results in the thread executing the for counter in 1..10 infinitely.

Also, each thread has their own 'scope' of variables. If you want to share a variable between threads you'll have to look into Arc and Mutex.

All threads needs to join().unwrap() for able to start

join does not execute a thread. It merely waits for a thread to finish.

1 Like

How can we start a thread without join?

Edit: I see everything now. All threads starting immediately when we spawn them. We just need to live the main thread enough. I changed code like that and all threads worked as long as 5 seconds.

    // ...main() function

    //    for (index, thread) in threads.into_iter().enumerate() {
    //        println!("Starting that thread: {index}");
    //        thread.join().map_err(|_| anyhow!("Thread error."))?;
    //    }
    thread::sleep(Duration::from_millis(5000));

    Ok(())
}

I was thinking that threads are similar to async functions but actually they are different. Threads are starting immediately when we spawn them. But async functions (functions which returns Future<> trait) doesn't start immediately. They need an executor or await syntax.

Thanks guys. Everything is fine for me now.

1 Like

thread::spawn() starts threads.

If you change the text

println!("Starting that thread: {index}");

to

println!("Waiting for finish of thread: {index}");

it would make more sense. (also delete loop so everything can finish.)

Indeed there's no such thing (in Rust) as a not-yet-started thread — but there are actually more similarities between thread usage and async than you think. These two lines do the same kind of thing in the same 3 steps, just with different types and syntax involved:

std::thread::spawn(   move || { do_something()       });
      tokio::spawn(async move { do_something().await });
//           └-┬-┘ └---┬----┘   └--------┬---------┘
//             │       │                 │
//             │       │              1. code to be run concurrently
//             │    2. an inert container for the code and data
//          3. spawn() takes the container and starts it running
2 Likes

maybe these threads are not start at the same time

No no that's not the main problem. I was guess that threads similar as futures. We need to join futures for start, I guess that this threads have same requirement. But now I understand that threads start immediately when we spawn them. Now everything fine.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.