Skip to content

Instantly share code, notes, and snippets.

@max-itzpapalotl
Last active February 11, 2024 17:06
Show Gist options
  • Save max-itzpapalotl/4e9e48ae99266f0a7b3f5c3149c5b896 to your computer and use it in GitHub Desktop.
Save max-itzpapalotl/4e9e48ae99266f0a7b3f5c3149c5b896 to your computer and use it in GitHub Desktop.
21. Async functions and tokio

21. Async functions and tokio

In this video I explain how to use async functions, a bit how they work and how to use a runtime from the tokio crate.

Our first async function

We are using the futures crate:

[dependencies]
futures = { version = "0.3.30", features = ["thread-pool"] }

And here is our first program:

use futures::executor::block_on;

async fn f(a: i64, b:i64) -> i64 {
    println!("Hello f");
    a+b
}

fn main() {
    let fut = f(1, 2);
    println!("Hello");
    let x = block_on(fut);
    println!("Done: {}", x);
}

As we see, futures are only executed if there is a runtime which polls them.

Async functions awaiting other async functions

In an async function, the .await method is a suspension point, the compiler generates a state machine for us.

use futures::executor::block_on;

async fn g(c: i64) -> i64 {
    println!("Hello g {}", c);
    c*c
}

async fn f(a: i64, b:i64) -> i64 {
    println!("Hello f");
    let aa = g(a).await;
    println!("Hello f2");
    let bb = g(b).await;
    println!("Hello f3");
    aa+bb
}

fn main() {
    let fut = f(1, 2);
    println!("Hello");
    let x = block_on(fut);
    println!("Done: {}", x);
}

Threadpool and multiple async functions running concurrently

use futures::executor::ThreadPool;

async fn g(c: i64) -> i64 {
    println!("Hello g {}", c);
    std::thread::sleep(std::time::Duration::from_secs(1));
    println!("Done g {}", c);
    c*c
}

async fn f(a: i64, b:i64) {
    println!("Hello f");
    let aa = g(a);
    let bb = g(b);
    println!("Hello f2");
    let x = futures::join!(aa, bb);
    println!("Hello f3: {}", x.0 + x.1);
}

fn main() {
    let fut1 = f(1, 2);
    let fut2 = f(3, 4);
    println!("Hello");
    let pool = ThreadPool::builder().pool_size(4).create().unwrap();
    pool.spawn_ok(fut1);
    pool.spawn_ok(fut2);
    std::thread::sleep(std::time::Duration::from_secs(10));
    println!("Done");
}

Here we see that two async functions run concurrently.

Using the tokio default runtime

Here we use this dependency:

[dependencies]
tokio = { version = "1.36.0", features = ["full"] }
async fn g(c: i64) -> i64 {
    println!("Hello g {}", c);
    std::thread::sleep(std::time::Duration::from_secs(c as u64));
    c*c
}

async fn f(a: i64, b:i64) -> i64 {
    println!("Hello f");
    let aa = g(a).await;
    println!("Hello f2");
    let bb = g(b).await;
    println!("Hello f3");
    aa+bb
}

#[tokio::main]
async fn main() {
    println!("Hello");
    let fut = f(1, 2);
    let fut2 = f(3, 4);
    println!("Hello2");
    let x = fut.await;
    println!("Hello3");
    let y = fut2.await;
    println!("Done: {} {}", x, y);
}

Spawning multiple async functions

async fn g(c: i64) -> i64 {
    println!("Hello g {}", c);
    std::thread::sleep(std::time::Duration::from_secs(c as u64));
    c*c
}

async fn f(a: i64, b:i64) -> i64 {
    println!("Hello f");
    let aa = g(a).await;
    println!("Hello f2");
    let bb = g(b).await;
    println!("Hello f3");
    aa+bb
}

#[tokio::main]
async fn main() {
    println!("Hello");
    let handle = tokio::spawn(f(1, 2));
    let handle2 = tokio::spawn(f(3, 4));
    println!("Hello2");
    let x = handle.await.unwrap();
    println!("Hello3");
    let y = handle2.await.unwrap();
    println!("Done: {} {}", x, y);
}

Starting a tokio runtime manually

async fn g(c: i64) -> i64 {
    println!("Hello g {}", c);
    std::thread::sleep(std::time::Duration::from_secs(c as u64));
    c*c
}

async fn f(a: i64, b:i64) -> i64 {
    println!("Hello f");
    let aa = g(a).await;
    println!("Hello f2");
    let bb = g(b).await;
    println!("Hello f3");
    aa+bb
}

fn main() {
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        println!("Hello");
        let handle = tokio::spawn(f(1, 2));
        let handle2 = tokio::spawn(f(3, 4));
        println!("Hello2");
        let x = handle.await.unwrap();
        println!("Hello3");
        let y = handle2.await.unwrap();
        println!("Done: {} {}", x, y);
    });
}

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment