なぜ非同期なのか?
Rust を使うと、高速で安全なソフトウェアが書けます。しかし、なぜ非同期コードを書くんでしょう?
非同期コードを使用すると、同じ OS のスレッドで複数のタスクを同時に実行できるようになる。 典型的なスレッドを使ったアプリケーションでは、2 つの Web ページを同時にダウンロードしたい時、次のように 2 つのスレッドに作業を分散します。
#![allow(unused_variables)]
fn main() {
fn get_two_sites() {
// 作業を行うために2つのスレッドを作成します。
let thread_one = thread::spawn(|| download("https:://www.foo.com"));
let thread_two = thread::spawn(|| download("https:://www.bar.com"));
// 両方のスレッドが完了するまで待ちます。
thread_one.join().expect("thread one panicked");
thread_two.join().expect("thread two panicked");
}
}
これは多くのアプリケーションでうまく機能する。スレッドはこれを行うように設計されているからね。複数の異なるタスクを同時に実行するようにね。
ただ、いくつか制限もあるんだ。スレッドの切り替えや、スレッド間でのデータ共有をするプロセスにはオーバーヘッドが結構あるのです…。
ただ何もしないで居座っているスレッドでさえ、貴重なシステムリソースを食いつぶします。これらは、非同期コードが排除するために設計されたコストです。私達は Rust のasync
/.await
記法を使って、先ほどのコードを書き換えることができます。それも、複数スレッドを作成することなく、一度に複数タスクを実行できるようにね。
#![allow(unused_variables)]
fn main() {
async fn get_two_sites_async() {
// 2つの異なるfutureを作成します。これらは、完了するまで実行すると
// 非同期でWebページをダウンロードします。
let future_one = download_async("https:://www.foo.com");
let future_two = download_async("https:://www.bar.com");
// 両方のfutureを同時に完了するまで実行します。
join!(future_one, future_two);
}
}
全体として、非同期アプリケーションはスレッド実装よりもすごく高速で、使うリソースも少ない可能性があります。ただし、コストがかかってしまいます。 スレッドは OS によってネイティブにサポートされているので、特別なプログラミングモデルは不必要です、どんな関数もスレッドを作成でき、通常の関数と同じくらい簡単にスレッドを使用する関数を呼び出すことが出来ます。
ただし、非同期関数は、言語もしくはライブラリの特別なサポートが必要になります。Rust では、async fn
がfuture
を返す非同期関数を作ってくれます。関数本体を実行するにはfuture
を最後まで実行する必要があります。
従来のスレッド化されたアプリケーションも非常に効果的で、Rust の小さなメモリ追跡と予測によってasync
を使わなくても十分である可能性を忘れないでください。
非同期プログラミングモデルによる複雑性の増加は、常にそれだけの価値があるか分からいないものです。単純なスレッドモデルの使用を考慮することも重要です。