このページの目次
tokio ランタイム
tokio の reactor(epoll/kqueue/IOCP ベース) は非同期 IO を担当し、executor は work-stealing 方式で Future をマルチスレッドにスケジューリングします。これらを合わせたものが Rust における非同期処理の事実上の標準ランタイムです。
spawnは Future をスレッドプールに送信し、select!は複数の非同期操作を同時に待機します。reactor と executor の役割分担を理解すれば、tokio のパフォーマンスモデルを理解したことになります。
tokio の3層アーキテクチャ
flowchart TD
TASK["🧵 あなたの async task<br/>tokio::spawn"]
TASK --> QUEUE["📋 Task Queue<br/>スケジューリング待ちの Future"]
QUEUE --> EXECUTOR["⚙️ Executor<br/>work-stealing スケジューラ<br/>CPUコア1つにつき1つのワーカースレッド"]
EXECUTOR --> REACTOR["🔌 Reactor<br/>epoll (Linux) / kqueue (macOS)<br/>IOCP (Windows)"]
REACTOR --> KERNEL["🐧 OS カーネル"]
REACTOR -.->|"IO 準備完了 → task をウェイクアップ"| QUEUE
classDef user fill:#e3f2fd,stroke:#1565c0
classDef middle fill:#fff3e0,stroke:#ef6c00
classDef os fill:#f3e5f5,stroke:#7b1fa2
class TASK,QUEUE user
class EXECUTOR,REACTOR middle
class KERNEL os
Reactor:登録されたすべての IO ソース(socket、timer、signal)を監視し、IO が準備完了になると、対応する task のウェイクアップを通知します。
Executor:task queue を管理し、ウェイクアップされた task に CPU タイムを再割り当てします。
あなたのコード:async fn と .await を呼び出すだけであり、reactor や executor と直接やり取りすることはありません。
2つのランタイム
// マルチスレッド (デフォルト)
async
// 同等の記述:
// シングルスレッド (単純なユースケース向け。スレッド間同期が不要な場合など)
async
spawn: 独立した並行タスク
let handle = spawn;
// handle: JoinHandle — .await して戻り値を取得可能
let result = handle.await.unwrap;
spawn されたタスクは異なるワーカースレッド上で実行される可能性があります。これは本物の並行処理であり、コルーチンではありません。返される JoinHandle は Future であり、.await することでタスクの完了を待機します。
Work Stealing スケジューラ
各ワーカースレッドには独自のローカルな task queue が存在します。あるワーカーがアイドル状態になると、他のワーカーのタスクをランダムに「盗み」ます。これは「1つのグローバルキュー + すべてのワーカーが競合する」モデルよりも効率的です。ローカルな push/pop はロックフリーであるため、ロック競合が減少します。
select!: 複数の Future を待機
select!
いずれかのブランチが完了すると、他のブランチはキャンセルされます(drop されます)。これは async Rust に組み込まれたキャンセルモデルです。明示的な cancellation token は不要です。キャンセルされた Future の Drop 実装がリソースのクリーンアップを担当します。
参考
- tokio: docs.rs/tokio, tokio.rs
- async book: rust-lang.github.io/async-book
Keywords: tokio, reactor, executor, work stealing, spawn, select!, runtime, current_thread