การแบ่งปันสถานะด้วย Arc/Mutex
ข้อมูลร่วมที่ปลอดภัย
การแบ่งปันสถานะด้วย Arc/Mutex เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดสถานะที่ใช้ร่วมกันจึงจัดการได้ยาก
บางครั้งการส่งข้อความก็ไม่เพียงพอ และมีหลายเธรดที่จำเป็นต้องอ่านและเขียนข้อมูลชุดเดียวกันจริง ๆ Rust จะไม่อนุญาตให้คุณแบ่งปันค่าที่เปลี่ยนแปลงได้ระหว่างเธรดโดยไม่มีการป้องกัน เพราะนั่นอาจทำให้เกิดการแข่งขันของข้อมูล
เครื่องมือสองอย่างที่ใช้ร่วมกันคือ:
Arcสำหรับแบ่งปันความเป็นเจ้าของระหว่างเธรดMutexสำหรับการแก้ไขข้อมูลแบบผูกขาดอย่างปลอดภัย
Rc ไม่ปลอดภัยสำหรับเธรด
Rc ช่วยให้มีความเป็นเจ้าของร่วมกันได้ แต่ใช้ได้กับเธรดเดียวเท่านั้น ตัวนับการอ้างอิงของมันไม่ได้รับการทำให้สอดคล้องกัน ดังนั้นคอมไพเลอร์จึงไม่ยอมให้ส่งมันข้ามเธรด หากต้องการแบ่งปันข้อมูลแบบหลายเธรด คุณต้องใช้ Arc (การนับการอ้างอิงแบบอะตอมิก)
Arc ทำงานคล้าย Rc แต่ปรับปรุงตัวนับด้วยการดำเนินการแบบอะตอมิก ทำให้การโคลนปลอดภัยระหว่างเธรด
use std::sync::Arc;
fn main() {
let data = Arc::new(vec![1, 2, 3]);
let clone1 = Arc::clone(&data);
println!("original: {:?}", data);
println!("clone: {:?}", clone1);
println!("count: {}", Arc::strong_count(&data));
}Mutex ให้สิทธิ์เข้าถึงแบบผูกขาด
Mutex ห่อหุ้มข้อมูลและรับประกันว่าจะมีเธรดเพียงหนึ่งเธรดเข้าถึงข้อมูลนั้นในแต่ละครั้ง คุณเรียก .lock() เพื่อรับ MutexGuard ซึ่งทำหน้าที่อ้างอิงไปยังค่าภายใน ล็อกจะถูกปล่อยโดยอัตโนมัติเมื่อการ์ดออกจากขอบเขต
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num += 10;
} // guard dropped here, lock released
println!("value = {:?}", m.lock().unwrap());
}การใช้ Arc และ Mutex ร่วมกัน
หากต้องการแบ่งปันข้อมูลที่เปลี่ยนแปลงได้ระหว่างเธรด ให้ห่อข้อมูลในรูปแบบ Arc:
Arcช่วยให้หลายเธรดถือแฮนเดิลไปยังข้อมูลชุดเดียวกันได้Mutexช่วยให้แต่ละเธรดแก้ไขข้อมูลได้อย่างปลอดภัยทีละเธรด
โคลน Arc สำหรับแต่ละเธรดก่อนสร้างเธรด
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut n = c.lock().unwrap();
*n += 1;
});
handle.join().unwrap();
println!("counter = {}", *counter.lock().unwrap());
}ตัวนับร่วมกันระหว่างหลายเธรด
ตัวอย่างคลาสสิกคือ เธรดสิบเธรดเพิ่มค่าตัวนับที่ใช้ร่วมกันทีละหนึ่ง ทุกเธรดถือสำเนา Arc ของตนเอง และล็อก Mutex เพื่อเพิ่มค่า หลังจากรอให้ทุกเธรดทำงานเสร็จ ยอดรวมจะเท่ากับ 10 พอดีโดยไม่มีการแข่งขันของข้อมูล
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = c.lock().unwrap();
*num += 1;
}));
}
for h in handles {
h.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}ขอบเขตของล็อกมีความสำคัญ
MutexGuard จะถือล็อกไว้จนกว่าจะถูกทำลาย หากถือมันไว้ระหว่างการทำงานที่ช้า เธรดอื่นจะถูกขัดขวาง ควรทำส่วนวิกฤตให้สั้น: ล็อก แก้ไข แล้วปล่อย การครอบการล็อกด้วยบล็อกขนาดเล็กจะช่วยให้ล็อกถูกปล่อยก่อนเริ่มการประมวลผลเพิ่มเติม
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let log = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for i in 0..3 {
let l = Arc::clone(&log);
handles.push(thread::spawn(move || {
{
let mut v = l.lock().unwrap();
v.push(i);
} // released quickly
}));
}
for h in handles { h.join().unwrap(); }
let mut result = log.lock().unwrap().clone();
result.sort();
println!("{:?}", result);
}ภาวะชะงักงัน: ความเสี่ยงที่เกิดขึ้นจริง
ภาวะชะงักงันเกิดขึ้นเมื่อเธรดสองเธรดต่างถือล็อกที่อีกเธรดหนึ่งต้องการ และทั้งคู่รอคอยตลอดไป Rust ป้องกันการแข่งขันของข้อมูลได้ แต่ไม่ได้ป้องกันภาวะชะงักงัน หลีกเลี่ยงปัญหานี้โดยล็อก Mutex หลายตัวตามลำดับเดียวกันเสมอ และถือเครื่องหมายล็อกไว้เป็นเวลาสั้น ๆ
นอกจากนี้ หลีกเลี่ยงการล็อก Mutex ตัวเดิมสองครั้งในเธรดเดียว เพราะ Mutex มาตรฐานไม่รองรับการล็อกซ้ำโดยเธรดเดิม
การทำให้ล็อกเสียเมื่อเธรดเกิดภาวะตื่นตระหนก
หากเธรดเกิดภาวะตื่นตระหนกขณะถือล็อก Mutex จะถูกทำให้เสีย การเรียก .lock() ในภายหลังจะคืนค่า Err เพื่อแจ้งให้ทราบว่าข้อมูลอาจไม่สอดคล้องกัน หากคุณตัดสินใจว่าข้อมูลยังใช้งานได้ คุณสามารถกู้คืนการ์ดภายในด้วย into_inner() จากข้อผิดพลาดนั้น
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(0));
let d = Arc::clone(&data);
let _ = thread::spawn(move || {
let mut g = d.lock().unwrap();
*g = 7;
panic!("boom"); // poisons the mutex
}).join();
match data.lock() {
Ok(g) => println!("ok: {}", *g),
Err(poisoned) => println!("recovered: {}", *poisoned.into_inner()),
}
}RwLock สำหรับผู้อ่านจำนวนมาก
เมื่อการอ่านมีจำนวนมากกว่าการเขียนอย่างชัดเจน RwLock อาจทำงานได้เร็วกว่า Mutex โดยอนุญาตให้มีผู้อ่านหลายรายพร้อมกันหรือผู้เขียนเพียงหนึ่งราย ใช้ .read() สำหรับการเข้าถึงร่วมกัน และใช้ .write() สำหรับการเข้าถึงแบบผูกขาด
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let config = Arc::new(RwLock::new(String::from("v1")));
let reader = Arc::clone(&config);
let r = thread::spawn(move || {
let val = reader.read().unwrap();
println!("read: {}", *val);
});
r.join().unwrap();
{
let mut w = config.write().unwrap();
*w = String::from("v2");
}
println!("final: {}", *config.read().unwrap());
}อะตอมิกสำหรับตัวนับอย่างง่าย
สำหรับตัวนับจำนวนเต็มเพียงตัวเดียว การใช้ Mutex เต็มรูปแบบถือว่าเกินความจำเป็น ชนิดข้อมูลอย่าง AtomicUsize มีการอัปเดตแบบไม่ใช้ล็อกผ่านเมธอด เช่น fetch_add ห่อชนิดข้อมูลเหล่านี้ด้วย Arc เพื่อแบ่งปันระหว่างเธรด เลือก Ordering ให้เหมาะสม โดย SeqCst เป็นค่าเริ่มต้นที่ปลอดภัยและเข้าใจง่ายที่สุด
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..5 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
c.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
println!("count = {}", counter.load(Ordering::SeqCst));
}การเลือกเครื่องมือที่เหมาะสม
แนวทางอย่างรวดเร็วสำหรับสถานะที่ใช้ร่วมกัน:
- Channel: ถ่ายโอนความเป็นเจ้าของในลักษณะไปป์ไลน์
- Arc<Mutex>: โครงสร้างที่เปลี่ยนแปลงได้และใช้ร่วมกัน โดยมีทั้งการอ่านและการเขียน
- Arc<RwLock>: ข้อมูลที่ใช้ร่วมกันและเน้นการอ่าน
- Atomics: ตัวนับหรือแฟล็กพื้นฐานแบบเดี่ยว
ควรเลือกเครื่องมือที่เรียบง่ายที่สุดและตรงกับงาน ใช้ล็อกเมื่อการส่งข้อความไม่สามารถจำลองปัญหาได้ดี
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับสถานะที่ใช้ร่วมกัน
ทบทวน
คุณได้เรียนรู้วิธีแบ่งปันสถานะระหว่างเธรดอย่างปลอดภัย:
Arcทำให้แบ่งปันความเป็นเจ้าของระหว่างเธรดได้อย่างปลอดภัย ส่วนRcทำไม่ได้Mutexให้สิทธิ์แก้ไขแบบผูกขาดผ่านการ์ดที่ปลดล็อกโดยอัตโนมัติArc<Mutex<T>>เป็นรูปแบบมาตรฐานสำหรับข้อมูลที่เปลี่ยนแปลงได้และใช้ร่วมกัน- ทำขอบเขตของล็อกให้สั้น และระวังภาวะชะงักงันกับการทำให้ล็อกเสีย
RwLockเหมาะกับข้อมูลที่เน้นการอ่าน ส่วนอะตอมิกเหมาะกับตัวนับอย่างง่าย
คำถามที่พบบ่อย
บทเรียน “การแบ่งปันสถานะด้วย Arc/Mutex” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การแบ่งปันสถานะด้วย Arc/Mutex” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การแบ่งปันสถานะด้วย Arc/Mutex”
ข้อมูลร่วมที่ปลอดภัย คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การแบ่งปันสถานะด้วย Arc/Mutex” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- แชนเนล mpsc
- การแบ่งปันสถานะด้วย Arc/Mutex
- เธรดตามขอบเขต
- แชนเนล Crossbeam