ทำงานกับ Futures และ Tasks
ทำความเข้าใจ trait `Future` และวิธีจัดตารางเวลาและจัดการภารกิจภายในสภาพแวดล้อมรันไทม์แบบอะซิงโครนัส
ทำงานกับ Futures และ Tasks เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 3 บทเรียน
Rust Future คืออะไร
ใน Rust แบบอะซิงโครนัส Future คือเทรตที่แสดงการคำนวณแบบอะซิงโครนัส ซึ่งอาจเสร็จสิ้นในภายหลัง ลองนึกภาพว่าเป็นคำสัญญาสำหรับค่าที่ยังไม่พร้อม
- เป็นองค์ประกอบพื้นฐานของ Rust แบบ async
async fns ใน Rust จะส่งคืนชนิดข้อมูลนิรนามที่นำเทรตFutureไปใช้จริง- ค่าจะไม่ถูกคำนวณทันที แต่จะถูกคำนวณเมื่อ
Futureถูก "ตรวจสอบ" โดยตัวประมวลผล
Futures ทำงานคืบหน้าได้อย่างไร
Future ไม่ได้ทำงานด้วยตัวเอง แต่ตัวประมวลผล (เช่น รันไทม์ Tokio) จะ "ตรวจสอบ" ซ้ำ ๆ เพื่อดูว่าการทำงานคืบหน้าหรือเสร็จสิ้นแล้วหรือไม่
- เมื่อถูกตรวจสอบ
Futureจะส่งคืนPoll::Pending(ยังไม่เสร็จ) หรือPoll::Ready(T)(เสร็จแล้ว นี่คือผลลัพธ์) - หากเป็น
Pendingตัวประมวลผลจะรู้ว่าควรตรวจสอบอีกครั้งในภายหลัง เมื่อมีเหตุการณ์ที่เกี่ยวข้องเกิดขึ้น (เช่น การนำเข้า/ส่งออกเสร็จสิ้น) - กลไกการตรวจสอบนี้ทำให้การดำเนินการแบบอะซิงโครนัสจำนวนมากทำงานพร้อมกันบนเธรดเดียวได้
โค้ด async ของคุณคือ Future
เมื่อคุณเขียน async fn Rust จะแปลงให้เป็นเครื่องจักรสถานะที่นำเทรต Future ไปใช้จริง การคำนวณจริงจะเริ่มต้นก็ต่อเมื่อมีการตรวจสอบ Future ที่ส่งคืนมา
มาดูฟังก์ชัน async แบบง่ายกัน ฟังก์ชันนี้จะไม่ทำงานจนกว่าจะมีตัวประมวลผลรอผลลัพธ์
async fn say_hello() -> String {
"Hello from a Future!".to_string()
}
#[tokio::main]
async fn main() {
let future = say_hello(); // This doesn't run the function yet!
println!("Future created, but not awaited.");
// To run it, an executor needs to poll it, often via .await
// let result = future.await;
// println!("{}", result);
}Futures กลายเป็นทาสก์
ขณะที่ Future คือคำจำกัดความของการคำนวณแบบ async ทาสก์คืออินสแตนซ์ที่กำลังทำงานของ Future นั้น ซึ่งถูกขับเคลื่อนจนเสร็จสิ้นโดยตัวประมวลผล
- เมื่อคุณสั่งให้ตัวประมวลผล (เช่น Tokio) เรียกใช้
Futureตัวประมวลผลจะห่อหุ้มมันไว้ในทาสก์ - จากนั้นตัวประมวลผลจะจัดการทาสก์นี้ โดยตรวจสอบเมื่อทาสก์พร้อมทำงานคืบหน้า
- ทาสก์คือหน่วยงานที่รันไทม์แบบ async จัดตารางเวลาและเรียกใช้พร้อมกัน
สร้างทาสก์เพื่อการทำงานพร้อมกัน
หากต้องการเรียกใช้ Future พร้อมกับโค้ดอื่น คุณต้อง "สร้าง" มันบนรันไทม์ Tokio ซึ่งจะสร้างทาสก์ใหม่ที่รันไทม์เป็นผู้จัดการ
ฟังก์ชัน tokio::spawn รับ Future และส่งคืน JoinHandle ซึ่งคุณใช้รอให้ทาสก์ทำงานเสร็จและรับผลลัพธ์ได้
async fn perform_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
format!("Task {} finished!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle = tokio::spawn(async {
perform_task(1).await
});
println!("Task 1 spawned.");
// We will await 'handle' in the next scene to get the result.
// let result = handle.await.unwrap();
// println!("{}", result);
}รับผลลัพธ์จากทาสก์
JoinHandle ที่ tokio::spawn ส่งคืนมานั้นเป็น Future ด้วย คุณสามารถใช้ .await กับแฮนเดิลนี้เพื่อรอให้ทาสก์ที่สร้างขึ้นทำงานเสร็จและรับค่าที่ส่งคืนมา
หากทาสก์ที่สร้างขึ้นเกิดข้อผิดพลาดร้ายแรง การรอ JoinHandle ของทาสก์นั้นจะส่งคืนข้อผิดพลาด
async fn perform_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
format!("Task {} finished!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle = tokio::spawn(async {
perform_task(1).await
});
println!("Task 1 spawned.");
let result = handle.await.unwrap(); // Await the handle to get the result
println!("{}", result);
println!("Main finished.");
}ทาสก์ที่ทำงานพร้อมกันหลายรายการ
ความสามารถของทาสก์และ tokio::spawn จะแสดงศักยภาพอย่างเต็มที่เมื่อคุณเรียกใช้การดำเนินการหลายรายการพร้อมกัน รันไทม์ Tokio จะสลับการทำงานระหว่างทาสก์อย่างมีประสิทธิภาพเมื่อทาสก์เหล่านั้นพร้อม
วิธีนี้ช่วยให้โปรแกรมของคุณดำเนินงานหลายอย่างไปพร้อมกันได้โดยไม่บล็อก แม้จะมีเพียงเธรดเดียว
async fn long_task(id: u8) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(100 + id as u64 * 50)).await;
format!("Long task {} done!", id)
}
#[tokio::main]
async fn main() {
println!("Main started.");
let handle1 = tokio::spawn(long_task(1));
let handle2 = tokio::spawn(long_task(2));
println!("Both tasks spawned.");
let result1 = handle1.await.unwrap();
let result2 = handle2.await.unwrap();
println!("{}\n{}", result1, result2);
println!("Main finished.");
}รอทั้งหมดด้วย join!
เมื่อคุณต้องรอให้ Futures หลายรายการเสร็จสิ้นพร้อมกัน tokio::join! เป็นมาโครที่มีประโยชน์ มาโครนี้รอ futures ที่ระบุทั้งหมดพร้อมกันและส่งคืนผลลัพธ์เป็นทูเพิล
การทำงานคล้ายกับการรอแฮนเดิลแต่ละรายการแยกกัน แต่กระชับกว่าสำหรับจำนวน futures ที่กำหนดตายตัว
async fn fetch_data(source: &str) -> String {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
format!("Data from {}", source)
}
#[tokio::main]
async fn main() {
println!("Starting data fetches...");
let (data_a, data_b) = tokio::join!(
fetch_data("Server A"),
fetch_data("Database B")
);
println!("Fetched: {}\nFetched: {}", data_a, data_b);
println!("All fetches complete.");
}จัดการข้อผิดพลาดของทาสก์
การดำเนินการแบบอะซิงโครนัสอาจล้มเหลวได้เช่นเดียวกับการดำเนินการแบบซิงโครนัส เป็นเรื่องปกติที่ Futures จะส่งคืนชนิด Result เพื่อระบุว่าสำเร็จหรือล้มเหลว
เมื่อรอ JoinHandle โปรดจำไว้ว่าผลลัพธ์จะถูกห่อหุ้มอยู่ใน Result อีกชั้นหนึ่ง เนื่องจากตัวทาสก์เองอาจเกิดข้อผิดพลาดร้ายแรง คุณมักจะเห็น handle.await? (หากอยู่ใน async fn ที่ส่งคืน Result) หรือ handle.await.unwrap() / .expect()
async fn might_fail(should_fail: bool) -> Result<String, &'static str> {
tokio::time::sleep(tokio::time::Duration::from_millis(30)).await;
if should_fail {
Err("Oops, something went wrong!")
} else {
Ok("Operation successful!".to_string())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let handle_ok = tokio::spawn(might_fail(false));
let handle_err = tokio::spawn(might_fail(true));
let result_ok = handle_ok.await??; // Await handle, then await inner Result
println!("{}", result_ok);
match handle_err.await? { // Await handle, then handle inner Result
Ok(val) => println!("{}", val),
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}ทำความเข้าใจ Futures และทาสก์
คุณได้เรียนรู้เกี่ยวกับ Futures ในฐานะการคำนวณ และทาสก์ในฐานะอินสแตนซ์ที่เรียกใช้การคำนวณเหล่านั้นแล้ว ข้อใดกล่าวถูกต้องเกี่ยวกับ tokio::spawn และ JoinHandle
ทบทวน: Futures และทาสก์
ยอดเยี่ยมครับ! ตอนนี้คุณเข้าใจแนวคิดหลักของ Futures และทาสก์ในระบบนิเวศอะซิงโครนัสของ Rust แล้ว:
Futureคือเทรตที่แสดงการคำนวณแบบอะซิงโครนัส ซึ่งท้ายที่สุดจะสร้างค่าออกมาasync fns จะถูกคอมไพล์เป็นชนิดข้อมูลที่นำเทรตFutureไปใช้จริง- ทาสก์คืออินสแตนซ์ของ
Futureที่ตัวประมวลผล (เช่น Tokio) จัดการและตรวจสอบอย่างต่อเนื่อง tokio::spawnใช้สร้างทาสก์ใหม่และส่งคืนJoinHandle- คุณใช้
.awaitกับJoinHandleเพื่อรับผลลัพธ์ของทาสก์ที่สร้างขึ้น
องค์ประกอบพื้นฐานเหล่านี้จำเป็นต่อการเขียนแอปพลิเคชัน Rust ที่มีประสิทธิภาพและไม่บล็อกการทำงาน!
เรียนรู้ Rust ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 39
- บทเรียน
- 144
คำถามที่พบบ่อย
บทเรียน “ทำงานกับ Futures และ Tasks” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ทำงานกับ Futures และ Tasks” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 3 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ทำงานกับ Futures และ Tasks”
ทำความเข้าใจ trait `Future` และวิธีจัดตารางเวลาและจัดการภารกิจภายในสภาพแวดล้อมรันไทม์แบบอะซิงโครนัส คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน
บทเรียน “ทำงานกับ Futures และ Tasks” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- รู้จัก Async/Await
- สร้างแอปพลิเคชันอะซิงโครนัสด้วย Tokio
- ทำงานกับ Futures และ Tasks