How to use `async fn` in `thread::spawn`

Hello, I'm newbie in Rust and trying to do some multithreading stuff.

Long story short, here's what I'm trying to accomplish :

use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::thread;

fn main() {
    let (tx, rx) = channel::<i32>();
    thread::spawn(move || stuff(tx));
    for val in rx {
        println!("{}", val);
    }
}

async fn stuff(tx: Sender<i32>) {
    tx.send(42);
    tx.send(69);
    tx.send(123);
}

But stuff() seems to never get executed. What am I missing ?
How do I execute a future async fn with thread::spawn ?

Thanks

Don't mark your function as async :slight_smile:
In your case, you just want to execute a "normal" (synchronous) function on another thread.

By declaring stuff to be async you make it return a Future, which is the encapsulation of an asynchronous operation. You would need to give that to an executor to actually execute it. When compiling your code, Rust should also issue a warning that the Future created by stuff(tx) is unused. Edit: it does not.

1 Like

Thanks for your fast answer !

It is a simplified version of what I'm trying to do, I made it async because the stuff function originally contained a call to async function reqwest::get("https://somestuff.com") and compiler required me for it to be encapsulated in async function.

Have good day

I managed to make it work by putting block_on in thread::spawn :

use futures::executor::block_on;
thread::spawn(move || block_on(stuff(tx)));

Is there a better way to do it or its the right way ?

Thanks

3 Likes

For now I would switch to reqwest::blocking::get which is a synchronous version of the function and avoid async until you know you want it.

Generally you don't want to be using block_on from futures. Use a real executor if you must use async code. In your case, you don't want async at all, so just use the blocking module.

1 Like

If you really want to use an async function better to use tokio
I guess this could be a minimal example

use tokio::runtime::Runtime;
use tokio::time::*;
fn main() {
    let mut rt = Runtime::new().unwrap();
    rt.block_on(async move {
        println!("hello from the async block");
        async_function("task0").await;

        //bonus, you could spawn tasks too
        tokio::spawn(async { async_function("task1").await });
        tokio::spawn(async { async_function("task2").await });
    });
    loop {}
}

async fn async_function(name: &str) {
    for i in 0..3 {
        println!("{} : {}", name, i);
        tokio::time::delay_for(Duration::from_secs(1)).await;
    }
}

This will print

hello from the async block
task0 : 0
task0 : 1
task0 : 2
task1 : 0
task2 : 0
task1 : 1
task2 : 1
task1 : 2
task2 : 2

Notice how you can await on an async function
additionally you can spawn a task to run simultaneously. See how task1 and task2 print together instead of one after another.

5 Likes

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.