ข้ามไปยังเนื้อหา

Threads

standard library ของ Rust มี OS thread ผ่าน std::thread::spawn คุณส่ง closure และฟังก์ชันจะส่งคืน JoinHandle<T> — handle ที่คุณใช้รอให้ thread เสร็จสิ้นและดึงค่าที่ส่งคืน

use std::thread;
fn main() {
let handle = thread::spawn(|| {
// นี่รันบน OS thread ใหม่
42
});
let result = handle.join().unwrap(); // บล็อกจนกว่า thread จะเสร็จ
println!("Thread returned: {}", result);
}

closure จะเป็น move || { ... } เมื่อต้องการ capture ตัวแปรจาก scope ที่ครอบอยู่ — ไม่เช่นนั้นคอมไพเลอร์ไม่สามารถรับประกันได้ว่าข้อมูลที่ถูก capture จะมีอายุยาวพอ

เนื่องจาก thread อาจมีอายุยาวกว่า scope ที่ถูก spawn ข้อมูลใดก็ตามที่ thread capture จะต้องเป็นเจ้าของโดย thread คำสั่ง move ย้าย ownership ของตัวแปรที่ถูก capture เข้าสู่ closure

use std::thread;
fn main() {
let message = String::from("hello from the main thread");
let handle = thread::spawn(move || {
// message ถูกย้ายมาที่นี่ — thread เป็นเจ้าของตัวเองแล้ว
println!("{}", message);
});
handle.join().unwrap();
// println!("{}", message); // จะไม่คอมไพล์ — ถูกย้ายไปแล้ว
}

เพื่อแชร์ข้อมูลที่ mutable ระหว่างหลาย thread ให้ห่อข้อมูลด้วย Arc<Mutex<T>>:

  • Mutex<T> ให้ mutual exclusion — มีแค่ thread เดียวที่สามารถล็อคได้ในแต่ละครั้ง
  • Arc<T> (Atomically Reference Counted) ให้หลาย thread ถือ pointer ที่นับ reference ไปยัง allocation เดียวกัน

คุณ clone Arc ก่อนย้าย clone นั้นเข้าไปในแต่ละ thread Mutex ที่อยู่ข้างในถูกแชร์ ไม่ใช่ clone

use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let counter = Arc::new(Mutex::new(0_i32));
let mut handles = Vec::new();
for _ in 0..4 {
let counter = Arc::clone(&counter); // เพิ่ม ref-count ราคาถูก
let h = thread::spawn(move || {
let mut n = counter.lock().unwrap(); // ขอ lock
*n += 1;
}); // lock ถูก release เมื่อ n drop
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
println!("Final count: {}", *counter.lock().unwrap());
}

รูปแบบที่พบบ่อยคือให้แต่ละ thread คำนวณค่าและ push เข้าไปใน Vec ที่แชร์กัน หลังจาก thread ทั้งหมดเสร็จสิ้น เรา sort ผลลัพธ์เพื่อให้ output เป็น deterministic ไม่ว่า scheduling order จะเป็นอย่างไร

use std::thread;
use std::sync::{Arc, Mutex};
fn main() {
let results = Arc::new(Mutex::new(Vec::new()));
let mut handles = Vec::new();
for i in 0..5 {
let results = Arc::clone(&results);
let handle = thread::spawn(move || {
let square = i * i;
let mut v = results.lock().unwrap();
v.push(square);
});
handles.push(handle);
}
for h in handles {
h.join().unwrap();
}
let mut final_results = results.lock().unwrap().clone();
final_results.sort();
println!("Squares: {:?}", final_results);
let sum: i32 = final_results.iter().sum();
println!("Sum: {}", sum);
}
JoinHandle::join() ทำอะไร?
ทำไม thread closure จึงมักใช้คำสั่ง move?
บทบาทของ Arc ใน Arc<Mutex<T>> คืออะไร?
ทำไมเราจึง sort ผลลัพธ์หลังจาก join thread ทั้งหมด?