နားလည်ထားရမယ့် အချက်
Lesson 18 ရဲ့ `std::thread` ဟာ OS thread တစ်ခုစီအတွက် memory/context-switch cost ရှိလို့ concurrent task ထောင်ချီ (ဥပမာ network request ထောင်ချီ) handle ချင်ရင် expensive ဖြစ်နိုင်ပါတယ်—async/await ကတော့ ဒီ scale ပြဿနာအတွက် lightweight solution တစ်ခုပါ။ `async fn` ကို ခေါ်တာနဲ့ code ချက်ချင်း run တာ မဟုတ်ဘဲ `Future` value တစ်ခု ချက်ချင်းပြန်ပေးတာသာ ဖြစ်ပါတယ်—Future ဟာ lazy ဖြစ်ပြီး `.await` (သို့) runtime က poll လုပ်မှသာ actual work စတင်ပါတယ်—ဒါက Lesson 16 ရဲ့ iterator ရဲ့ lazy evaluation နှင့် တူတဲ့ pattern တစ်ခုပါ။ JavaScript/Go လို language တွေမှာ async runtime (event loop, goroutine scheduler) က language ကိုယ်တိုင် built-in ပါလာပေမယ့် Rust ကတော့ deliberately runtime ကို language core ထဲ ထည့်မထားပါဘူး—Rust ကို embedded system အထိ အသုံးချနိုင်ဖို့ (runtime overhead လုံးဝမလိုချင်တဲ့ context) library level မှာသာ runtime ကို ရွေးချယ်စေချင်လို့ပါ—ဒါကြောင့် `tokio` (အသုံးအများဆုံး) လို external crate ကို depend ပေါ်ထားပြီးမှသာ async code ကို actually execute နိုင်ပါတယ်။ `.await` ကတော့ Future ကို complete ဖြစ်အောင် wait ပေးတဲ့ point တစ်ခုဖြစ်ပြီး OS thread ကို block မလုပ်ဘဲ (thread ဟာ တခြား task ဆီ ပြန်လှည့်ပြီး အလုပ်လုပ်နိုင်) runtime scheduler ဆီ control ကို ပြန်ပေးလိုက်တာဖြစ်ပါတယ်—ဒါကြောင့် I/O-bound task (network call, file read) ရာနှင့်ချီကို OS thread အနည်းငယ်တည်းနဲ့ efficiently handle နိုင်ပါတယ်—CPU-bound work အတွက်တော့ Lesson 18 ရဲ့ thread က ပိုသင့်တော်ပါတယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Lesson 22 ရဲ့ Axum API project ကို preview လုပ်ကြည့်ရင် HTTP handler function တစ်ခုစီကို `async fn get_lesson(...)` အဖြစ် ရေးရမှာဖြစ်ပါတယ်—request ရာနှင့်ချီကို တစ်ပြိုင်နက် handle ချင်ရင် (database query စောင့်နေတဲ့ time ကို OS thread ကို block မလုပ်ဘဲ) async ကို လိုအပ်ပါတယ်။ Lesson content file များစွာကို disk ကနေ concurrently ဖတ်ချင်ရင် `tokio::fs::read_to_string(path).await` ကို `Vec` of futures အဖြစ် ဆောက်ပြီး `futures::future::join_all(...)` ဖြင့် တစ်ပြိုင်နက် await လုပ်နိုင်ပါတယ်—Lesson 18 ရဲ့ thread-per-file approach ထက် lightweight ဖြစ်ပါတယ်။ `#[tokio::main]` attribute ကို `main` function ပေါ်တွင် ထားရင် tokio runtime ကို auto-setup လုပ်ပေးပါတယ်—ဒီ setup မရှိရင် `async fn` ကို ဘယ်လိုမှ execute လို့ ရမှာမဟုတ်ပါ။
အတူတူ စမ်းရေးကြည့်မယ်
use tokio::time::{sleep, Duration};
async fn fetch_lesson_stat(id: u32) -> String {
sleep(Duration::from_millis(10)).await;
format!("lesson-{id} ready")
}
#[tokio::main]
async fn main() {
let a = fetch_lesson_stat(1);
let b = fetch_lesson_stat(2);
let (result_a, result_b) = tokio::join!(a, b);
println!("{result_a}");
println!("{result_b}");
}"lesson-1 ready" နှင့် "lesson-2 ready" ကို concurrently (sequential 20ms အစား ~10ms အတွင်း) print ထုတ်နိုင်မည်။၅ မိနစ် စမ်းကြည့်
`async fn double(n: u32) -> u32` ရေးပါ (small `sleep` ပါစေ)—`tokio::join!` ဖြင့် ဒီ function ကို input value သုံးခုနှင့် concurrently ခေါ်ပြီး result သုံးခုကို print ထုတ်ပါ—`#[tokio::main]` setup လိုအပ်ကြောင်း သတိပြုပါ။
သတိလေးတစ်ချက်
`#[tokio::main]` (သို့ runtime setup အခြားနည်း) မထားဘဲ `async fn main()` ကို run ကြိုးစားခြင်း—compile error "async main function is not supported" ရနိုင်ပါတယ်—Rust language core မှာ runtime built-in မရှိလို့ဖြစ်ပါတယ်။
Async task များစွာကို `.await` တစ်ခုချင်းစီ sequential ခေါ်ခြင်း (`a.await; b.await;`)—concurrent ဖြစ်စေချင်ရင် `tokio::join!` သို့ `join_all` ကို သုံးရမယ်—sequential await ကတော့ concurrency ဘာမှ မရှိတော့ပါ။