Async Introduction
Async/Await และ Futures
หัวข้อที่มีชื่อว่า “Async/Await และ Futures”syntax async/.await ของ Rust ให้คุณเขียนโค้ด non-blocking ที่ดูเหมือนโค้ด sequential ธรรมดา ภายใต้ประทุน async fn ส่งคืน Future — ค่าที่แสดงถึงการคำนวณที่อาจยังไม่เสร็จสมบูรณ์
use tokio::time::{sleep, Duration};
async fn fetch_data(id: u32) -> String { sleep(Duration::from_millis(10)).await; format!("data for id={}", id)}
#[tokio::main]async fn main() { let result = fetch_data(1).await; println!("{}", result);}คำสั่ง .await ระงับ async function ปัจจุบันจนกว่า Future ที่ถูกนำไปใช้จะ resolve ขณะที่ระงับอยู่ executor (tokio) สามารถรัน task อื่น ๆ ได้
Future คืออะไร?
หัวข้อที่มีชื่อว่า “Future คืออะไร?”Future<Output = T> เป็น trait ที่แสดงถึงค่าประเภท T ที่จะพร้อมในบางจุดในอนาคต การเรียก async fn ไม่ได้รัน body ทันที — แต่ส่งคืน Future กลับมา body จะรันเฉพาะตอนคุณ .await (หรือ poll ผ่าน executor)
// ยังไม่ทำอะไร — แค่สร้าง Futurelet fut = fetch_data(42);
// นี่รัน body และบล็อกจนเสร็จlet result = fut.await;การประเมินแบบ lazy นี้คือเหตุผลที่ async ของ Rust มีต้นทุนเป็นศูนย์: future ที่ไม่เคย await จะไม่รันเลย
การรัน Task พร้อมกันด้วย tokio::join!
หัวข้อที่มีชื่อว่า “การรัน Task พร้อมกันด้วย tokio::join!”เพื่อรัน async task หลายตัวในเวลาเดียวกัน ใช้ tokio::join! ซึ่ง drive future ทั้งหมดพร้อมกันบน thread เดียว (หรือข้าม worker thread ขึ้นอยู่กับ runtime configuration)
use tokio::time::{sleep, Duration};
async fn task(name: &str, ms: u64) -> String { sleep(Duration::from_millis(ms)).await; format!("{} done", name)}
#[tokio::main]async fn main() { let (a, b, c) = tokio::join!( task("alpha", 30), task("beta", 20), task("gamma", 10), ); println!("{}", a); println!("{}", b); println!("{}", c);}task ทั้งสามเริ่มทันที tokio::join! รอจนทุกตัวเสร็จและส่งคืนผลลัพธ์ตามลำดับ
การ Spawn Async Task
หัวข้อที่มีชื่อว่า “การ Spawn Async Task”tokio::spawn เปิดตัว task บน tokio thread pool แล้วส่งคืน JoinHandle คล้ายกับ std::thread::spawn
use tokio::task;
#[tokio::main]async fn main() { let handle = task::spawn(async { // รันพร้อมกันบน tokio thread pool 42_u32 });
let result = handle.await.unwrap(); println!("Spawned task returned: {}", result);}async vs Threads: ควรใช้อะไร
หัวข้อที่มีชื่อว่า “async vs Threads: ควรใช้อะไร”| เกณฑ์ | OS Threads (std::thread) | Async (tokio) |
|---|---|---|
| งาน CPU-bound | แนะนำ | ใช้ spawn_blocking |
| งาน I/O-bound | สิ้นเปลือง (thread ถูกบล็อก) | แนะนำ |
| จำนวน task พร้อมกัน | หลายร้อย (จำกัดโดย OS) | หลายล้าน (น้ำหนักเบา) |
| ขนาด stack | ~8 MB ต่อ thread | ~ไม่กี่ KB ต่อ task |
| ต้องการ external crate | ไม่ | ใช่ (tokio) |
การเพิ่ม tokio ในโปรเจกต์
หัวข้อที่มีชื่อว่า “การเพิ่ม tokio ในโปรเจกต์”cargo add tokio --features fullหรือใน Cargo.toml:
[dependencies]tokio = { version = "1", features = ["full"] }จากนั้น annotate entry point ของคุณ:
#[tokio::main]async fn main() { // โค้ด async ของคุณอยู่ที่นี่}#[tokio::main] คือ procedural macro ที่ห่อ async fn main ของคุณในการเรียก tokio::runtime::Runtime::block_on เพื่อเริ่ม tokio event loop