ข้ามไปยังเนื้อหา

Async Introduction

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<Output = T> เป็น trait ที่แสดงถึงค่าประเภท T ที่จะพร้อมในบางจุดในอนาคต การเรียก async fn ไม่ได้รัน body ทันที — แต่ส่งคืน Future กลับมา body จะรันเฉพาะตอนคุณ .await (หรือ poll ผ่าน executor)

// ยังไม่ทำอะไร — แค่สร้าง Future
let fut = fetch_data(42);
// นี่รัน body และบล็อกจนเสร็จ
let result = fut.await;

การประเมินแบบ lazy นี้คือเหตุผลที่ async ของ Rust มีต้นทุนเป็นศูนย์: future ที่ไม่เคย await จะไม่รันเลย

เพื่อรัน 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! รอจนทุกตัวเสร็จและส่งคืนผลลัพธ์ตามลำดับ

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);
}
เกณฑ์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)
Terminal window
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

async fn ใน Rust ส่งคืนอะไร?
ทำไม Rust จึงต้องการ external executor อย่าง tokio เพื่อรัน async code?
ความแตกต่างระหว่าง tokio::join! และการ await แบบ sequential คืออะไร?
เมื่อใดควรใช้ spawn_blocking แทน tokio::spawn ธรรมดา?