Learn Rust Coding · บทเรียน

ทำงานกับ Futures และ Tasks

ทำความเข้าใจ trait `Future` และวิธีจัดตารางเวลาและจัดการภารกิจภายในสภาพแวดล้อมรันไทม์แบบอะซิงโครนัส

บทเรียน 3 จาก 311 ขั้นตอน

ทำงานกับ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รู้จัก Async/Await
  2. สร้างแอปพลิเคชันอะซิงโครนัสด้วย Tokio
  3. ทำงานกับ Futures และ Tasks
← กลับไปที่ Learn Rust Coding