0Pricing
Learn Rust Coding · บทเรียน

การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด

เจาะลึกองค์ประกอบพื้นฐานของ Rust สำหรับการเขียนโปรแกรมพร้อมกัน รวมถึงเธรดและการส่งข้อความ ซึ่งช่วยรับรองว่าไม่มีการแย่งเข้าถึงข้อมูล

การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 3 บทเรียน

บทนำสู่การทำงานพร้อมกัน

ยินดีต้อนรับสู่ การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด! ในบทเรียนนี้ เราจะเรียนรู้วิธีทำให้โปรแกรม Rust ของคุณทำหลายสิ่งพร้อมกัน

การทำงานพร้อมกัน หมายถึงการดำเนินการคำนวณหลายรายการเสมือนเกิดขึ้นในเวลาเดียวกัน ซึ่งมีความสำคัญต่อแอปพลิเคชันที่ตอบสนองได้ดีและการใช้ประโยชน์จากหน่วยประมวลผลหลายแกนสมัยใหม่

  • เธรด คือหน่วยการทำงานขนาดเล็กภายในโปรแกรม
  • แต่ละเธรดสามารถเรียกใช้ส่วนต่าง ๆ ของโค้ดแยกจากกันได้
  • ระบบความเป็นเจ้าของของ Rust ช่วยป้องกันข้อผิดพลาดทั่วไปในการทำงานพร้อมกัน

การสร้างเธรดแรกของคุณ

Rust มี std::thread::spawn สำหรับสร้างเธรดใหม่ คุณส่งคลอเชอร์ (ฟังก์ชันนิรนาม) ที่มีโค้ดซึ่งเธรดใหม่ควรเรียกใช้ให้กับมัน

ลองเรียกใช้ตัวอย่างนี้เพื่อดูการทำงานของเธรด:

use std::thread;
use std::time::Duration;

fn main() {
  println!("Hello from the main thread!");

  thread::spawn(|| {
    for i in 1..=5 {
      println!("Hi number {} from the spawned thread!", i);
      thread::sleep(Duration::from_millis(1));
    }
  });

  for i in 1..=3 {
    println!("Hi number {} from the main thread!", i);
    thread::sleep(Duration::from_millis(1));
  }
}

การรอเธรดด้วย `join`

ในตัวอย่างก่อนหน้านี้ เธรดที่สร้างขึ้นอาจทำงานไม่เสร็จก่อนที่เธรดหลักจะจบการทำงาน เนื่องจากโดยค่าเริ่มต้นเธรดหลักจะไม่รอเธรดที่สร้างขึ้น

เพื่อให้แน่ใจว่าเธรดที่สร้างขึ้นทำงานจนเสร็จ เราใช้เมธอด join() กับ JoinHandle เมธอดนี้จะหยุดเธรดปัจจุบันไว้จนกว่าเธรดที่เข้าร่วมจะทำงานเสร็จ

use std::thread;
use std::time::Duration;

fn main() {
  let handle = thread::spawn(|| {
    for i in 1..=5 {
      println!("Thread: {}", i);
      thread::sleep(Duration::from_millis(1));
    }
  });

  for i in 1..=3 {
    println!("Main: {}", i);
    thread::sleep(Duration::from_millis(1));
  }

  handle.join().unwrap(); // Wait for the spawned thread to finish
  println!("Spawned thread has finished!");
}

การย้ายข้อมูลเข้าเธรด

เมื่อคุณใช้ตัวแปรจากสภาพแวดล้อมภายในคลอเชอร์ spawn กฎความเป็นเจ้าของของ Rust จะเข้ามามีบทบาท โดยค่าเริ่มต้น คลอเชอร์จะพยายามยืมตัวแปร

หากเธรดที่สร้างขึ้นมีอายุยืนยาวกว่าขอบเขตการใช้งานของเธรดหลักที่ตัวแปรถูกกำหนดไว้ อาจทำให้เกิดการอ้างอิงที่ห้อยอยู่ได้ วิธีแก้คือใช้คีย์เวิร์ด move ก่อนพารามิเตอร์ของคลอเชอร์

  • move บังคับให้คลอเชอร์รับความเป็นเจ้าของตัวแปรที่จับไว้
  • วิธีนี้ทำให้ข้อมูลใช้งานได้ตลอดอายุการใช้งานของเธรดใหม่
use std::thread;

fn main() {
  let data = String::from("Hello from outer scope");

  let handle = thread::spawn(move || { // Use 'move' to take ownership of 'data'
    println!("Data in thread: {}", data);
  });

  handle.join().unwrap();
  // println!("Data after thread: {}", data); // This would cause a compile error!
  println!("Main thread finished.");
}

การสื่อสารด้วยการส่งข้อความ

แม้ Rust จะอนุญาตให้แบ่งปันข้อมูลโดยตรงระหว่างเธรด (สถานะที่ใช้ร่วมกัน) แต่จำเป็นต้องมีการซิงโครไนซ์อย่างระมัดระวัง (เช่น ใช้ Mutex และ Arc ซึ่งจะกล่าวถึงในบทเรียนอื่น)

แนวทางที่ปลอดภัยกว่าและมักเรียบง่ายกว่าสำหรับการทำงานพร้อมกันคือ การส่งข้อความ เธรดจะสื่อสารกันด้วยการส่งข้อความถึงกัน ทำให้ไม่ต้องเข้าถึงหน่วยความจำที่ใช้ร่วมกันโดยตรง

  • เธรดหนึ่งส่งข้อมูล
  • อีกเธรดหนึ่งรับข้อมูล
  • ไลบรารีมาตรฐานของ Rust มี std::sync::mpsc สำหรับการทำงานนี้

การตั้งค่าช่องสัญญาณ `mpsc`

std::sync::mpsc ย่อมาจาก ผู้ผลิตหลายราย ผู้บริโภครายเดียว ซึ่งหมายความว่าเธรดหลายรายการสามารถส่งข้อความได้ แต่มีเธรดเพียงรายการเดียวที่รับข้อความได้

หากต้องการสร้างช่องสัญญาณ ให้เรียกใช้ mpsc::channel() ซึ่งจะคืนค่าทูเพิลที่ประกอบด้วย:

  • ตัวส่ง (tx): ใช้สำหรับส่งข้อความ
  • ตัวรับ (rx): ใช้สำหรับรับข้อความ

มาดูวิธีสร้างช่องสัญญาณและส่งข้อความง่าย ๆ กัน

use std::sync::mpsc;
use std::thread;

fn main() {
  // Create a new channel
  let (tx, rx) = mpsc::channel();

  thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap(); // Send the message
    // println!("val is {}", val); // Error: val moved to tx.send()
  });

  // Receive the message in the main thread
  let received = rx.recv().unwrap();
  println!("Got: {}", received);
}

การส่งข้อความหลายรายการ

คุณสามารถส่งข้อความหลายรายการผ่านช่องสัญญาณเดียวกันได้ ตัวรับมีเมธอดสำหรับจัดการเรื่องนี้:

  • rx.recv(): หยุดเธรดปัจจุบันไว้จนกว่าจะได้รับข้อความ และคืนค่าเป็น Result
  • rx.try_recv(): ไม่หยุดการทำงาน โดยจะคืนค่า Result ทันที ซึ่งอาจเป็นข้อความหรือข้อผิดพลาดหากไม่มีข้อความพร้อมใช้งาน
  • คุณยังสามารถวนซ้ำโดยตรงผ่านตัวรับเพื่อรับข้อความไปเรื่อย ๆ จนกว่าตัวส่งจะปิด
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
  let (tx, rx) = mpsc::channel();

  thread::spawn(move || {
    let msgs = vec!["hi", "from", "the", "thread"];
    for msg in msgs {
      tx.send(String::from(msg)).unwrap();
      thread::sleep(Duration::from_millis(10));
    }
  });

  // Iterate over the receiver to get all messages
  for received in rx {
    println!("Got: {}", received);
  }
  println!("All messages received!");
}

ผู้ผลิตหลายราย ผู้บริโภครายเดียว

ตัวอักษร 'MP' ใน mpsc หมายถึง ผู้ผลิตหลายราย คุณสามารถ clone Sender เพื่อให้เธรดหลายรายการส่งข้อความไปยัง Receiver เดียวกันได้

Sender แต่ละรายการที่ได้จากการ clone จะแชร์การเข้าถึงช่องสัญญาณเดียวกัน ทำให้ส่งข้อความพร้อมกันจากเธรดต่าง ๆ ได้

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
  let (tx, rx) = mpsc::channel();

  let tx1 = tx.clone(); // Clone the sender for another thread
  thread::spawn(move || {
    tx1.send(String::from("hello from tx1")).unwrap();
  });

  thread::spawn(move || {
    tx.send(String::from("hello from tx2")).unwrap();
  });

  // Collect all messages
  let mut received_messages: Vec<String> = vec![];
  for received in rx {
    received_messages.push(received);
    if received_messages.len() == 2 { break; } // Assuming 2 messages for this example
  }
  println!("Received: {:?}", received_messages);
}

การทำงานพร้อมกันอย่างไร้กังวลด้วย Rust

ระบบความเป็นเจ้าของและระบบชนิดข้อมูลของ Rust มีความสำคัญอย่างยิ่งต่อการทำให้เกิด การทำงานพร้อมกันอย่างไร้กังวล ด้วยการบังคับใช้กฎขณะคอมไพล์ Rust จึงป้องกันข้อผิดพลาดทั่วไปในการทำงานพร้อมกัน เช่น การแข่งกันเข้าถึงข้อมูล

  • ความเป็นเจ้าของ ป้องกันไม่ให้มีการอ้างอิงแบบเปลี่ยนแปลงได้หลายรายการไปยังข้อมูลเดียวกัน
  • คีย์เวิร์ด move ทำให้แน่ใจว่าข้อมูลถูกโอนย้ายไปยังเธรดใหม่อย่างปลอดภัย
  • การส่งข้อความ (mpsc) หลีกเลี่ยงการใช้หน่วยความจำร่วมกันโดยสิ้นเชิง ทำให้การสื่อสารปลอดภัยตั้งแต่การออกแบบ

กลไกเหล่านี้ช่วยให้คุณเขียนโค้ดที่ทำงานพร้อมกันได้อย่างมั่นใจ โดยรู้ว่าคอมไพเลอร์จะตรวจพบปัญหาที่อาจเกิดขึ้นได้หลายประการ

ตรวจสอบแนวคิดการทำงานพร้อมกัน

ข้อความใดต่อไปนี้เกี่ยวกับโครงสร้างพื้นฐานสำหรับการทำงานพร้อมกันของ Rust เป็นจริง

ทบทวน: เธรดและการส่งข้อความ

ทำได้ดีมาก! คุณได้เรียนรู้พื้นฐานของการทำงานพร้อมกันอย่างไร้กังวลใน Rust แล้ว:

  • เราสร้างเธรดใหม่โดยใช้ std::thread::spawn
  • เราใช้ JoinHandle::join() เพื่อรอให้เธรดทำงานเสร็จ
  • คีย์เวิร์ด move โอนความเป็นเจ้าของข้อมูลเข้าไปในเธรดอย่างปลอดภัย
  • std::sync::mpsc มีระบบส่งข้อความที่มีประสิทธิภาพสำหรับการสื่อสารระหว่างเธรด
  • กฎความเป็นเจ้าของของ Rust ป้องกันการแข่งกันเข้าถึงข้อมูลขณะคอมไพล์ ทำให้การเขียนโปรแกรมแบบทำงานพร้อมกันปลอดภัยยิ่งขึ้น

บทเรียนนี้มอบพื้นฐานที่มั่นคงสำหรับการสร้างแอปพลิเคชันที่ตอบสนองได้ดีและมีประสิทธิภาพ ฝึกฝนต่อไปนะครับ

คำถามที่พบบ่อย

บทเรียน “การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 3 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด”

เจาะลึกองค์ประกอบพื้นฐานของ Rust สำหรับการเขียนโปรแกรมพร้อมกัน รวมถึงเธรดและการส่งข้อความ ซึ่งช่วยรับรองว่าไม่มีการแย่งเข้าถึงข้อมูล คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน

บทเรียน “การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม

ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. ตัวชี้อัจฉริยะ Box, Rc และ Arc
  2. การเปลี่ยนแปลงค่าภายใน: RefCell, Cell
  3. การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด
← กลับไปที่ Learn Rust Coding