การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด
เจาะลึกองค์ประกอบพื้นฐานของ 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(): หยุดเธรดปัจจุบันไว้จนกว่าจะได้รับข้อความ และคืนค่าเป็นResultrx.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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ตัวชี้อัจฉริยะ Box, Rc และ Arc
- การเปลี่ยนแปลงค่าภายใน: RefCell, Cell
- การทำงานพร้อมกันอย่างไร้กังวลด้วยเธรด