async/.await 입문

async/.await는 비동기 함수를 동기 코드처럼 보이도록 해주기 위한 Rust의 내장 툴입니다. async는 코드 블록을 Future라는 trait을 구현하는 상태 머신으로 변환해 줍니다. 동기 함수에서 blocking함수의 호출은 전체 스레드를 blocking하는 반면에 blocking된 Future는 스레드의 제어를 내어 놓아 다른 타스크의 실행을 허용합니다. 이는 다른 Future의 실행을 허용합니다.

비동기 함수를 만들려면 async fn 구문을 사용할 수 있습니다 :


#![allow(unused)]
fn main() {
async fn do_something() { /* ... */ }
}

async fn에 의해 반환되는 값은 Future입니다. 무슨 일이 일어나려면 Future는 executor 프로그램에서 실행 되어야 합니다.

// `block_on` blocks the current thread until the provided future has run to
// completion. Other executors provide more complex behavior, like scheduling
// multiple futures onto the same thread.
use futures::executor::block_on;

async fn hello_world() {
    println!("hello, world!");
}

fn main() {
    let future = hello_world(); // Nothing is printed
    block_on(future); // `future` is run and "hello, world!" is printed
}

async fn 안에서 .await를 사용하여 Future trait을 구현하는 다른 type (예 : 또 다른async fn의 반환 값)의 실행이 완성되기를 기다릴 수 있습니다. block_on과 달리.await는 현재의 스레드를 block하지 않습니다. 대신 Future가 완료 될 때 까지 비동기 적으로 기다립니다. 이는 만약 현재의 future가 진행될 수 없는 경우 다른 타스크가 실행되도록 해줍니다.

예를 들어, 우리가 3개의 async fn: learn_song, sing_song, 그리고 dance 를 가지고 있다고 해봅시다.

async fn learn_song() -> Song { /* ... */ }
async fn sing_song(song: Song) { /* ... */ }
async fn dance() { /* ... */ }

배우고, 노래하고, 춤을 추는 한 가지 방법은 각각을 개별적으로 block하는 것입니다:

fn main() {
    let song = block_on(learn_song());
    block_on(sing_song(song));
    block_on(dance());
}

그러나 우리는 이런 식으로 최고의 성능을 제공하지 않습니다. 한 번에 한 가지 작업만 수행하고 있습니다! 분명히 우리는 노래를 부르기 전에 노래를 배워야 합니다. 하지만 우리는 노래를 배우거나 부르는 동시에 춤을 출 수 있습니다. 이를 위해 동시에 실행할 수 있는 두 개의 async fn을 만들 수 있습니다:

async fn learn_and_sing() {
    // Wait until the song has been learned before singing it.
    // We use `.await` here rather than `block_on` to prevent blocking the
    // thread, which makes it possible to `dance` at the same time.
    let song = learn_song().await;
    sing_song(song).await;
}

async fn async_main() {
    let f1 = learn_and_sing();
    let f2 = dance();

    // `join!` is like `.await` but can wait for multiple futures concurrently.
    // If we're temporarily blocked in the `learn_and_sing` future, the `dance`
    // future will take over the current thread. If `dance` becomes blocked,
    // `learn_and_sing` can take back over. If both futures are blocked, then
    // `async_main` is blocked and will yield to the executor.
    futures::join!(f1, f2);
}

fn main() {
    block_on(async_main());
}

이 예에서 노래를 부르기 전에 노래를 배우는 것이 필요하지만 노래 학습과 노래 부르기는 춤과 동시에 일어날 수 있습니다. 우리가 learn_and_singlearn_song().await 대신 block_on(learn_song())을 사용했다면, learn_song이 실행되는 동안 스레드는 다른 작업을 수행 할 수 없습니다. 이렇게 하면 노래와 동시에 춤을 출 수 없게됩니다. learn_song future를 .await~ing 함으로써, 우리는 learn_song이 block 된 경우 다른 작업이 현재 스레드를 인계받을 수 있도록 할 수 있습니다. 이를 통해 여러 타스크를 동일한 스레드에서 동시에 운영 할 수 있습니다. 이제 async/await의 기본 사항을 배웠으므로 예제를 살펴봅시다.