Use async/await in a spawned thread

I'm fairly new the the async world and I think I just don't know the right pattern for what I'm trying to achieve.

I'd like to have a periodic background timer which fires an HTTP-request. I'm not interested in the result and I don't care if this blocks or if the timings are precise. The method for firing the request is async.

This is the stripped down version

thread::spawn(|| {
    loop {
        HTTP_CLIENT.keep_alive(); // .await required
        thread::sleep(Duration::from_secs(60));
    }
});

If I make the closure itself async, I have to .join().await the surrounding thread to even start it.

thread::spawn(|| async {
    loop {
        HTTP_CLIENT.keep_alive().await;
        thread::sleep(Duration::from_secs(60));
    }
});

My intention is to just busy-loop (with thread::sleep) for the keep_alive's Future to complete. But I don't know how to do that either.

I got it working and I think I understand why.

thread::spawn(|| {
    let runtime = tokio::runtime::Runtime::new().unwrap();            
    loop {
        let _ = runtime.block_on(runtime.spawn(async move {
            HTTP_CLIENT.keep_alive().await
        }));            
        thread::sleep(Duration::from_secs(60));
    }
});

I'm not sure if this is the best solution. Looks cumbersome to me.

You shouldn't sleep inside an async context, see Async: What is blocking? – Alice Ryhl for an in-depth look at why.

How about using a tokio::task and tokio::time::Interval:

    tokio::task::spawn(async {
        let mut interval = tokio::time::interval(std::time::Duration::from_secs(60));
        loop {
            interval.tick().await;
            HTTP_CLIENT.keep_alive().await;
        }
    });

First, using tokio::spawn instead of thread::spawn is the probably the best option here.

If you really want to run it on an actual dedicated thread, I would recommend obtaining a Handle to an existing runtime and using the block_on method from that runtime. If you don't have an existing runtime, I would recommend creating a current_thread runtime instead. For more details on this topic, see Bridging with sync code from the Tokio website.

Note that you shouldn't be using tokio::spawn if you really want a dedicated thread. Using tokio::spawn will run it on one of the runtime's own worker threads instead.

It's actually ok in this case because the use of the block_on call makes it happen outside the runtime's async context.

2 Likes

Thank you both for your help!
Using tokio::spawn with an async looks ideomatic and I think I begin to understand how the async infrastructure works. Moving to an async closure made me switch to time::interval which now feels natural as well.

I finally have to deep dive into tokio as it's becoming ubiquitous - no more excuses :slight_smile:

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.