async
/ .await
入門!
async/.await
は通常の同期コードの用に見える非同期関数を作成するための Rust のビルドインツールです。 async
はコードブロックをFuture
トレイトを実装しているステートマシンに変換するものです。 一方、同期メソッドでブロッキング関数を呼び出すとスレッド全体がブロックされてしまいますが、ブロックされたFuture
はスレッドの制御をもたらし、他のFuture
を実行できるようにします。
非同期関数を作成するには次のasync fn
構文を使用できます。
# #![allow(unused_variables)] #fn main() { async fn do_something() { ... } #}
async fn
によってこの関数の返り値はFuture
になります。
Future
は次のようにエグゼキューターで実行する必要があります。
// block_onは提供されたfutureが完了するまで現在のスレッドをブロックします。 // 他のエグゼキューターは、同じスレッドに複数のfutureをスケジュールするなど、 // より複雑な動作を提供します。 use futures::executor::block_on; async fn hello_world() { println!("hello, world!"); } fn main() { let future = hello_world(); // ここでは何もprintされない block_on(future); // futureが動き"hello, world!"が表示される }
async fn
内では.await
を使うことで、ほかのFuture
トレイトを実装する別の型の完了を待つことができます。block_on
とは異なり、.await
は現在のスレッドをブロックしません、代わりに、Future
が完了するのを非同期で待機し、Future
が現在進行できないときは他のタスクを実行できるようにします。
例として、3 つのasync fn
を考えてみましょう。learn_song
, sing_song
, dance
です。
# #![allow(unused_variables)] #fn main() { async fn learn_song() -> Song { ... } async fn sing_song(song: Song) { ... } async fn dance() { ... } #}
歌、学習、ダンスを行う方法の一つは、それぞれ個別にブロックすることです。
fn main() { let song = block_on(learn_song()); block_on(sing_song(song)); block_on(dance()); }
ただ、この方法では最高のパフォーマンスを実現しているわけではありません。一つのことしか実行してないからね!
明らかに、歌を歌うには歌を学ぶ必要があります。しかし、歌を学んだあとに歌うのと、同時に踊ることも可能ですよね?
これを行うには、同時に実行できる 2 つの独立した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_sing
でlearn_song().await
ではなくblock_on(learn_son())
を使ってしまうと、スレッドはしばらくの間他のことを行うことができなくなり、同時に踊ることを不可能にします。
今学習した、async / await
の例を試してみましょう!