非同期エコシステム
Rust は現在、async コードを書くための最低限の要素のみを提供しています。 重要なこととして、エグゼキューター、タスク、リアクター、コンビネーター、低レベル I/O の Future とトレイトは まだ標準ライブラリでは提供されていません。当面は、 コミュニティが提供する非同期エコシステムがこれらの不足を補います。
Async Foundations Team は、Async Book の例を拡張して複数のランタイムを扱えるようにすることに関心があります。 このプロジェクトへの貢献に関心がある場合は、 Zulip でご連絡ください。
非同期ランタイム
非同期ランタイムは、非同期アプリケーションを実行するために使用されるライブラリです。 ランタイムは通常、リアクター と 1 つ以上の エグゼキューター をまとめて提供します。 リアクターは、非同期 I/O、プロセス間通信、タイマーなどの外部イベントに対する購読メカニズムを提供します。 非同期ランタイムでは、購読者は通常、低レベル I/O 操作を表す Future です。 エグゼキューターは、タスクのスケジューリングと実行を処理します。 エグゼキューターは、実行中および一時停止中のタスクを追跡し、Future を完了するまでポーリングし、進行可能になったタスクを起床します。 「executor」という語は、「runtime」と同じ意味で使われることがよくあります。 ここでは、互換性のあるトレイトや機能とともにランタイムをまとめたものを表すために、「エコシステム」という語を使用します。
コミュニティ提供の非同期クレート
Futures クレート
futures クレート には、async コードを書くために有用なトレイトと関数が含まれています。
これには、Stream、Sink、AsyncRead、AsyncWrite トレイト、およびコンビネーターなどのユーティリティが含まれます。
これらのユーティリティやトレイトは、最終的に標準ライブラリの一部になる可能性があります。
futures には独自のエグゼキューターがありますが、独自のリアクターはないため、非同期 I/O やタイマー Future の実行はサポートしていません。
このため、完全なランタイムとは見なされません。
一般的な選択肢は、futures のユーティリティを別のクレートのエグゼキューターと併用することです。
広く使われている非同期ランタイム
標準ライブラリには非同期ランタイムはなく、公式に推奨されているものもありません。 次のクレートは、広く使われているランタイムを提供します。
- Tokio: HTTP、gRPC、tracing フレームワークを備えた、広く使われている非同期エコシステムです。
- async-std: 標準ライブラリのコンポーネントに対応する非同期版を提供するクレートです。
- smol: 小さく簡素化された非同期ランタイムです。
UnixStreamやTcpListenerのような構造体をラップするために使用できるAsyncトレイトを提供します。 - fuchsia-async: Fuchsia OS で使用するためのエグゼキューターです。
エコシステムの互換性を判断する
すべての非同期アプリケーション、フレームワーク、ライブラリが互いに、またはすべての OS やプラットフォームと互換性があるわけではありません。 ほとんどの非同期コードは任意のエコシステムで使用できますが、一部のフレームワークやライブラリでは特定のエコシステムを使用する必要があります。 エコシステム上の制約は必ずしも文書化されているわけではありませんが、 ライブラリ、トレイト、関数が特定のエコシステムに依存しているかどうかを判断するための経験則はいくつかあります。
非同期 I/O、タイマー、プロセス間通信、またはタスクとやり取りする async コードは、 一般的に特定の非同期エグゼキューターまたはリアクターに依存します。 async 式、コンビネーター、同期型、ストリームなど、その他すべての async コードは、 入れ子になった Future もエコシステムに依存していない限り、通常はエコシステムに依存しません。 プロジェクトを始める前に、関連する非同期フレームワークやライブラリについて調査し、 選択したランタイムおよび相互の互換性を確認することをお勧めします。
特に、Tokio は mio リアクターを使用し、
AsyncRead と AsyncWrite を含む独自バージョンの非同期 I/O トレイトを定義しています。
それ自体では、async-std や smol とは互換性がありません。
これらは async-executor クレート と、futures で定義されている AsyncRead および AsyncWrite
トレイトに依存しているためです。
競合するランタイム要件は、
あるランタイム向けに書かれたコードを別のランタイム内で呼び出せるようにする互換性レイヤーによって解決できる場合があります。
たとえば、async_compat クレート は、
Tokio とその他のランタイムの間に互換性レイヤーを提供します。
async API を公開するライブラリは、特定のエグゼキューターやリアクターに依存すべきではありません。 ただし、タスクを生成する必要がある場合や、独自の非同期 I/O またはタイマー Future を定義する必要がある場合は除きます。 理想的には、タスクのスケジューリングと実行の責任はバイナリのみが担うべきです。
シングルスレッドエグゼキューターとマルチスレッドエグゼキューター
非同期エグゼキューターは、シングルスレッドまたはマルチスレッドにできます。
たとえば、async-executor クレートには、シングルスレッドの LocalExecutor とマルチスレッドの Executor の両方があります。
マルチスレッドエグゼキューターは、複数のタスクを同時に進行させます。 多数のタスクを含むワークロードでは実行を大幅に高速化できますが、 タスク間でデータを同期するコストは通常高くなります。 シングルスレッドランタイムとマルチスレッドランタイムのどちらを選ぶかを決める際には、 アプリケーションのパフォーマンスを測定することをお勧めします。
タスクは、それを作成したスレッド上で実行することも、別のスレッド上で実行することもできます。
非同期ランタイムは、多くの場合、タスクを別のスレッドに生成する機能を提供します。
タスクが別のスレッドで実行される場合でも、非ブロッキングであるべきです。
マルチスレッドエグゼキューター上でタスクをスケジュールするには、それらのタスクは Send でもなければなりません。
一部のランタイムは、非 Send タスクを生成する関数を提供しており、
これにより、すべてのタスクがそれを生成したスレッド上で実行されることが保証されます。
また、専用スレッド上にブロッキングタスクを生成する関数を提供することもあります。
これは、他のライブラリのブロッキングな同期コードを実行する場合に便利です。