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

Threads

ใน Node.js หรือ browser คุณรัน JavaScript แบบ parallel บน CPU core อื่นตรง ๆ ไม่ได้ จะ spawn Web Worker ก็ทำได้ แต่ Worker รันอยู่ในกระบวนการแยก: แชร์ closure หรือส่ง reference ข้ามไปไม่ได้ — ส่งได้เฉพาะข้อมูลที่ serialize ได้ผ่าน postMessage เท่านั้น

Rust ให้ OS threads จริง ผ่าน std::thread::spawn threads เหล่านี้รันในกระบวนการเดียวกัน แชร์ address space เดียวกัน และสามารถรับค่า (ไม่ใช่สำเนา) จาก parent thread ได้ — ตราบใดที่ ownership rules ถูกต้อง

thread::spawn รับ closure และรันบน OS thread ใหม่ closure ต้อง move ข้อมูลที่ต้องการจาก scope รอบๆ เพราะ thread อาจมีชีวิตนานกว่า stack frame ปัจจุบัน

การเรียกจะคืนค่า JoinHandle<T> โดยที่ T คือ return type ของ closure การเรียก .join() บน handle จะ block จนกว่า thread จะเสร็จและคืนค่า Result<T, Box<dyn Any>>

TypeScript
// TypeScript (Node.js): ไม่มี real threads
// Web Workers รันในกระบวนการแยก
// และสื่อสารได้เฉพาะผ่าน postMessage (ข้อมูล serialized)
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';
if (isMainThread) {
const worker = new Worker(__filename, { workerData: { value: 10 } });
worker.on('message', (result) => console.log('result:', result));
} else {
// ใน worker — ไม่สามารถแชร์ closure หรือ reference จาก main thread ได้
parentPort?.postMessage(workerData.value * 2);
}
Rust
use std::thread;
fn main() {
let value = 10u32; // owned by main thread
// move closure รับ ownership ของ 'value'
let handle = thread::spawn(move || {
// 'value' ถูก own โดย thread นี้แล้ว
value * 2
});
// join() block จนกว่า thread จะเสร็จ
// และคืนค่า return value ของ closure
let result = handle.join().unwrap();
println!("result: {result}"); // result: 20
}

Pattern ที่พบบ่อยคือ spawn worker threads จำนวนหนึ่ง รวบรวม JoinHandle ทั้งหมดไว้ใน Vec แล้ว join ทั้งหมดและ aggregate ผลลัพธ์

TypeScript
// TypeScript: งาน parallel ผ่าน Promise.all
async function sumChunks(data: number[][]): Promise<number> {
const partials = await Promise.all(
data.map(chunk =>
new Promise<number>(resolve =>
resolve(chunk.reduce((a, b) => a + b, 0))
)
)
);
return partials.reduce((a, b) => a + b, 0);
}
Rust
use std::thread;
fn main() {
let chunks: Vec<Vec<u32>> = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
let handles: Vec<_> = chunks
.into_iter()
.map(|chunk| {
thread::spawn(move || chunk.iter().sum::<u32>())
})
.collect();
let total: u32 = handles
.into_iter()
.map(|h| h.join().unwrap())
.sum();
println!("total: {total}"); // total: 45
}
use std::thread;
fn main() {
let chunks: Vec<Vec<u32>> = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
let handles: Vec<_> = chunks
.into_iter()
.map(|chunk| {
thread::spawn(move || chunk.iter().sum::<u32>())
})
.collect();
let total: u32 = handles
.into_iter()
.map(|h| h.join().unwrap())
.sum();
println!("total: {total}");
}
`thread::spawn` ใน Rust คืนค่าอะไร?
ทำไม closure ที่ส่งให้ thread::spawn ต้องใช้ keyword `move`?
`.join()` บน JoinHandle ทำอะไร?
JavaScript ทำงานแบบ CPU-parallel จริงได้อย่างไร?