How to make tokio spawn execute asynchronously?

Hi guys, I'm learning tokio recently, but I encountered some incomprehensible problems, such as the sequential execution of code in tokio::spawn.

#[tokio::test]
async fn test_tokio() {
    use std::thread;
    use std::time::Duration;

    let r = tokio::spawn(async move {
        for i in (1..10).rev() {
            println!("hi number {} from the spawned thread!", i);
            // let _ = tokio::time::sleep(tokio::time::Duration::from_secs(i)).await;
            thread::sleep(Duration::from_secs(i));
            println!("bye number {} from the spawned thread!", i);
        }
    });

    let _ = tokio::join!(r);
    // let _ = r.await;
    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        let _ = tokio::time::sleep(tokio::time::Duration::from_millis(1));
    }
}

What I expect is that the functions in spawn are executed at the same time, but this code is indeed executed one by one. The next one will not start until the previous one is executed, so why is this?

You are only spawning a single task and performing a normal, sequential loop inside it. You are not spawning multiple tasks. And a simple for loop does not loop in parallel.

(Oh, and you are also blocking inside the spawned task by using thread::sleep, which means that even if you had multiple tasks, you would block the runtime in each of them anyway.)

3 Likes

I think you should at least read

1 Like

Thank you for your reply, is there any sleep method that is non-blocking please?

I have change my test code like:

#[tokio::test]
async fn test_tokio() {
    use std::thread;
    use std::time::Duration;


    let mut handles = Vec::new();
    for i in 1..10 {
        let r = tokio::spawn(async move {
            println!("hi number {} from the spawned thread!", i);
            // let _ = tokio::time::sleep(tokio::time::Duration::from_secs(i)).await;
            // thread::sleep(Duration::from_secs(1));
            println!("bye number {} from the spawned thread!", i);
        });
        handles.push(r);
    }

    // let _ = tokio::join!(r);
    for i in handles {
        i.await.unwrap();
    }
}

But they still run one by one:

hi number 1 from the spawned thread!
bye number 1 from the spawned thread!
hi number 2 from the spawned thread!
bye number 2 from the spawned thread!
hi number 3 from the spawned thread!
bye number 3 from the spawned thread!
hi number 4 from the spawned thread!
bye number 4 from the spawned thread!
hi number 5 from the spawned thread!
bye number 5 from the spawned thread!
hi number 6 from the spawned thread!
bye number 6 from the spawned thread!
hi number 7 from the spawned thread!
bye number 7 from the spawned thread!
hi number 8 from the spawned thread!
bye number 8 from the spawned thread!
hi number 9 from the spawned thread!
bye number 9 from the spawned thread!

Using std::thread::sleep in async code is incorrect. You can use tokio::time::sleep instead. See this article.

No, they are running in parallel. For example, I got this:

hi number 1 from the spawned thread!
bye number 1 from the spawned thread!
hi number 4 from the spawned thread!
bye number 4 from the spawned thread!
hi number 5 from the spawned thread!
bye number 5 from the spawned thread!
hi number 6 from the spawned thread!
bye number 6 from the spawned thread!
hi number 7 from the spawned thread!
bye number 7 from the spawned thread!
hi number 8 from the spawned thread!
bye number 8 from the spawned thread!
hi number 9 from the spawned thread!
bye number 9 from the spawned thread!
hi number 2 from the spawned thread!
bye number 2 from the spawned thread!
hi number 3 from the spawned thread!
bye number 3 from the spawned thread!

Notice the ordering. The fact that they're not interleaved is just because the two printlns happen so close to each other. They will sometimes be interleaved, even if it is rare.

2 Likes

Thank you so much for your answer, you solved a problem that was bugging me all day! :kissing_heart: