แชนเนล mpsc
ส่งข้อมูลระหว่างเธรด
แชนเนล mpsc เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
Channel คืออะไร
channel คือท่อทางเดียวสำหรับส่งค่าจากเธรดหนึ่งไปยังอีกเธรดหนึ่ง ไลบรารีมาตรฐานของ Rust มี std::sync::mpsc โดย mpsc หมายถึง ผู้ผลิตหลายราย ผู้บริโภครายเดียว
- ฝั่ง Sender ใช้ส่งค่าเข้าไป
- ฝั่ง Receiver ใช้รับค่าออกมา
Channel ช่วยให้เธรดสื่อสารกันด้วยการ ส่งข้อความ แทนการใช้หน่วยความจำร่วมกันโดยตรง จึงหลีกเลี่ยง data race ได้หลายกรณี
สร้าง Channel
เรียก mpsc::channel() เพื่อรับทูเพิล (Sender, Receiver) ในที่นี้เราจะส่งค่าหนึ่งค่าจากเธรดที่สร้างขึ้นกลับมายังเธรดหลัก
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(42).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}send และ recv
tx.send(value) คืนค่าเป็น Result และจะล้มเหลวเฉพาะเมื่อ receiver ถูกทิ้งไปแล้ว rx.recv() จะ บล็อก จนกว่าจะมีค่ามาถึง และคืนค่า Err เมื่อ sender ทั้งหมดหายไป
sendโอนสิทธิ์ความเป็นเจ้าของค่าเข้าไปใน channelrecvรับสิทธิ์ความเป็นเจ้าของออกมาที่อีกฝั่ง
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let msg = String::from("hello from thread");
tx.send(msg).unwrap();
});
let text = rx.recv().unwrap();
println!("{}", text);
}สิทธิ์ความเป็นเจ้าของเคลื่อนผ่าน Channel
เนื่องจาก send รับค่าไปเป็นเจ้าของ คุณจึงใช้ค่านั้นหลังส่งไม่ได้ กฎขณะคอมไพล์นี้รับประกันว่าจะไม่มีเธรดใดเก็บการอ้างอิงเก่าของข้อมูลที่อีกเธรดเป็นเจ้าของอยู่
ด้านล่าง หากพยายามพิมพ์ msg หลัง send จะเกิดข้อผิดพลาดขณะคอมไพล์ ดังนั้นเราจะใช้ค่านี้เพียงครั้งเดียว
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let data = vec![1, 2, 3];
tx.send(data).unwrap();
// data is moved; cannot use it here
});
let v = rx.recv().unwrap();
println!("Sum: {}", v.iter().sum::<i32>());
}วนซ้ำผ่าน Receiver
Receiver implement IntoIterator การวนซ้ำผ่านมันจะคืนค่าแต่ละค่าจนกว่า channel จะปิด (sender ทั้งหมดถูกทิ้งไปแล้ว) นี่คือวิธีมาตรฐานในการรับกระแสข้อความมาใช้งาน
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
for i in 1..=3 {
tx.send(i).unwrap();
}
});
for received in rx {
println!("Received: {}", received);
}
}ผู้ผลิตหลายรายด้วย clone
mp ใน mpsc หมายความว่าคุณสามารถมี sender ได้หลายตัว ให้ clone Sender แล้วมอบสำเนาให้แต่ละเธรด receiver จะรวบรวมทุกค่าจนกว่า clone ทุกตัวจะถูกทิ้ง
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let tx2 = tx.clone();
thread::spawn(move || { tx.send("from A").unwrap(); });
thread::spawn(move || { tx2.send("from B").unwrap(); });
for msg in rx {
println!("{}", msg);
}
}พฤติกรรมเมื่อ Channel ปิด
ลูปรับค่าจะจบลงโดยอัตโนมัติเมื่อ sender ทั้งหมด ถูกทิ้ง หากยังมี Sender อยู่แม้เพียงหนึ่งตัว for msg in rx จะบล็อกตลอดไปเพื่อรอค่าเพิ่มเติม คุณต้องทิ้ง sender หรือกำหนดขอบเขตการใช้งานให้ถูกต้องเสมอ เพื่อให้ลูปจบลง
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
let handle = thread::spawn(move || {
for i in 0..3 {
tx.send(i * 10).unwrap();
}
// tx dropped here, closing the channel
});
handle.join().unwrap();
let total: i32 = rx.iter().sum();
println!("Total: {}", total);
}try_recv สำหรับการอ่านแบบไม่บล็อก
recv จะบล็อก แต่ try_recv จะคืนค่าทันทีเป็น Result โดยให้ Ok(value) หากมีข้อความพร้อมอ่าน หรือให้ Err หาก channel ว่างหรือขาดการเชื่อมต่อ เหมาะสำหรับลูปเหตุการณ์ที่ต้องทำงานอื่นต่อไป
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
thread::sleep(Duration::from_millis(50));
tx.send("ready").unwrap();
});
loop {
match rx.try_recv() {
Ok(msg) => { println!("{}", msg); break; }
Err(_) => println!("waiting..."),
}
thread::sleep(Duration::from_millis(20));
}
}ส่งชนิดข้อมูลแบบกำหนดเอง
ชนิดข้อมูลใด ๆ ที่เป็น Send สามารถส่งผ่าน channel ได้ รวมถึง struct และ enum ที่คุณสร้างเอง enum เหมาะอย่างยิ่งสำหรับจำลองชนิดข้อความที่แตกต่างกันในโพรโทคอลของ worker
use std::sync::mpsc;
use std::thread;
enum Job {
Print(String),
Add(i32, i32),
}
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
tx.send(Job::Print(String::from("hi"))).unwrap();
tx.send(Job::Add(2, 3)).unwrap();
});
for job in rx {
match job {
Job::Print(s) => println!("print: {}", s),
Job::Add(a, b) => println!("add: {}", a + b),
}
}
}sync_channel และแรงต้านกลับ
mpsc::sync_channel(n) สร้าง channel แบบ จำกัดขนาด ที่มีบัฟเฟอร์ขนาด n เมื่อบัฟเฟอร์เต็ม send จะบล็อกจนกว่าจะมีพื้นที่ว่าง วิธีนี้ทำให้เกิด แรงต้านกลับ เพื่อป้องกันไม่ให้ผู้ผลิตที่เร็วส่งข้อมูลจนผู้บริโภคที่ช้ารับไม่ไหว
sync_channel(0)เป็น channel แบบนัดพบ: send และ recv ต้องมาถึงพร้อมกัน
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::sync_channel(2);
thread::spawn(move || {
for i in 1..=4 {
tx.send(i).unwrap();
println!("sent {}", i);
}
});
for v in rx {
println!("got {}", v);
}
}รูปแบบ Worker อย่างง่าย
แชนเนลเหมาะอย่างยิ่งกับรูปแบบ ผู้ผลิต/ผู้บริโภค: เธรดหนึ่งสร้างรายการงานขึ้นมา ขณะที่อีกเธรดหนึ่งรับและประมวลผลรายการเหล่านั้น ในตัวอย่างนี้ เธรดหลักสร้างตัวเลข และเธรดผู้ปฏิบัติงานยกกำลังสองตัวเลขแต่ละตัว แล้วส่งผลลัพธ์กลับผ่านแชนเนลอีกช่องหนึ่ง
use std::sync::mpsc;
use std::thread;
fn main() {
let (job_tx, job_rx) = mpsc::channel();
let (res_tx, res_rx) = mpsc::channel();
thread::spawn(move || {
for n in job_rx {
res_tx.send(n * n).unwrap();
}
});
for n in 1..=4 {
job_tx.send(n).unwrap();
}
drop(job_tx);
for r in res_rx {
println!("square: {}", r);
}
}ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับแชนเนล mpsc
ทบทวน
คุณได้เรียนรู้แนวคิดสำคัญของแชนเนล mpsc:
mpsc::channel()คืนค่าคู่(Sender, Receiver)sendนำค่าเข้าไป ส่วนrecvจะรอเพื่อรับค่าออกมา- การวนซ้ำผ่านตัวรับจะใช้ข้อความจนกว่าผู้ส่งทั้งหมดจะถูกทำลาย
- โคลน
Senderเพื่อให้มีผู้ผลิตหลายราย try_recvจะไม่บล็อก ส่วนsync_channel(n)จะเพิ่มแรงดันย้อนกลับแบบมีขอบเขต
แชนเนลช่วยให้เธรดแบ่งปันข้อมูลได้อย่างปลอดภัยด้วยการส่งต่อความเป็นเจ้าของ แทนการแบ่งปันหน่วยความจำ
คำถามที่พบบ่อย
บทเรียน “แชนเนล mpsc” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แชนเนล mpsc” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แชนเนล mpsc”
ส่งข้อมูลระหว่างเธรด คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “แชนเนล mpsc” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ