နားလည်ထားရမယ့် အချက်
`std::thread::spawn` ဖြင့် OS thread တစ်ခုကို run လုပ်ချင်ရင် closure တစ်ခုကို pass ပေးရပါတယ်—အဲဒီ closure က main thread ရဲ့ variable ကို capture လုပ်ချင်ရင် `move` keyword ကို explicit ထည့်ရပါတယ်—ownership ကို spawn ဖြစ်တဲ့ thread ဆီ လုံးဝ move ပေးလိုက်ခြင်းဖြစ်ပြီး main thread ဘက်က ဒီ variable ကို ဆက်သုံးမရတော့ပါ—ဒါက Lesson 6 ရဲ့ ownership rule ကို thread boundary တစ်ခုပေါ်မှာ ထပ်ကျင့်နေတာပါ။ `Send` trait (thread တစ်ခုကနေ တစ်ခုဆီ ownership လွှဲပြောင်းလို့ safe ဖြစ်ကြောင်း) နှင့် `Sync` trait (reference ကို thread များစွာက တစ်ပြိုင်နက် access လို့ safe ဖြစ်ကြောင်း) ကို type အများစုက automatically implement ရပေမယ်—`Rc<T>` လို type တွေကတော့ deliberately implement မလုပ်ထားလို့ multi-thread context မှာ သုံးကြိုးစားရင် compiler က compile error ချက်ချင်းပြပေးပါတယ်—ဒါက data race ဆိုတာ runtime မှာ intermittent ဖြစ်တတ်တဲ့ bug (debug ဖို့ အလွန်ခက်ခဲတတ်) အနေအစား compile time error တစ်ခုပဲ ဖြစ်လာစေတဲ့ Rust ရဲ့ signature guarantee ပါ။ `mpsc` (multiple producer, single consumer) channel ကတော့ thread များကြား shared mutable state (locks လိုအပ်) မသုံးဘဲ message ပို့ခြင်းဖြင့် data ကို ဆက်သွယ်ခွင့်ပေးတဲ့ pattern ဖြစ်ပြီး—`tx.send(value)` ကနေ ownership ကို channel ဆီ move ပေးလိုက်ပြီး `rx.recv()` ဘက်ကနေ ownership ကို ရယူနိုင်ပါတယ်—"shared memory ကနေ communicate" မလုပ်ဘဲ "communicate ကနေ memory share" ဆိုတဲ့ Go language ရဲ့ CSP philosophy (channel) နှင့် ဆင်တူပါတယ်၊ ဒါပေမယ့် Rust မှာ ownership move ကို compile time မှာ enforce ထားလို့ channel ကနေတစ်ဆင့် ပို့ထားတဲ့ data ကို sender ဘက်ကနေ ထပ်သုံးလို့ လုံးဝမရတော့ပါ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Concurrent batch processor (Lesson 23 ရဲ့ project) ကို preview လုပ်ကြည့်ရင် lesson file batch တစ်ခုကို `std::thread::spawn` ဖြင့် thread များစွာအတွင်း word-count ရေတွက်ရန် ခွဲခြားနိုင်ပါတယ်—file list ကို `move` closure ထဲ ownership move လုပ်ပေးလိုက်ရင် main thread က ဒီ list ကို ဆက်ရည်ညွှန်းလို့ မရတော့ပါ။ Thread တစ်ခုစီရဲ့ word count result ကို main thread ဆီ ပြန်ပို့ချင်ရင် `mpsc::channel()` ဖန်တီးပြီး `tx.clone()` ကို thread တစ်ခုစီအတွက် ထုတ်ကူးမယ်—thread တစ်ခုစီက ရလဒ်ကို `tx.send(count)` ဖြင့် ပို့ပြီး main thread က `rx.iter()` ဖြင့် accumulate မယ်—shared `Mutex` state လုံးဝမလိုအပ်ဘဲ message-passing တစ်ခုတည်းနဲ့ result aggregate လို့ရပါတယ်။ `Rc<Vec<String>>` (Lesson 17 ကနေ) ကို thread များကြား share ကြိုးစားရင် compile error ချက်ချင်းပြမှာဖြစ်လို့ `Arc<Vec<String>>` (atomic reference count) ကို အသုံးပြုရမှာဖြစ်ပါတယ်။
အတူတူ စမ်းရေးကြည့်မယ်
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
for batch in [vec!["a", "bb", "ccc"], vec!["dddd", "e"]] {
let tx = tx.clone();
thread::spawn(move || {
let count: usize = batch.iter().map(|w| w.len()).sum();
tx.send(count).unwrap();
});
}
drop(tx);
let total: usize = rx.iter().sum();
println!("total chars: {total}");
}"total chars: 11" ကို print ထုတ်နိုင်မည်—thread နှစ်ခုက channel ကနေတစ်ဆင့် result ပို့ပြီး main thread က accumulate လုပ်ခဲ့ကြောင်း သက်သေပြမည်။၅ မိနစ် စမ်းကြည့်
`thread::spawn` ဖြင့် thread သုံးခု spawn ပါ—thread တစ်ခုစီက numeric value တစ်ခု (ကိုယ်တိုင်ရွေးချယ်) ကို `mpsc::channel` ဖြင့် main thread ဆီ ပို့ပါ—main thread က `rx.iter().sum()` ဖြင့် ရလဒ်တွေကို ပေါင်းပါ။
သတိလေးတစ်ချက်
`thread::spawn` closure ထဲက outer variable ကို `move` keyword မထည့်ဘဲ borrow ကြိုးစားခြင်း—spawn ဖြစ်တဲ့ thread ဟာ main thread ထက် ပိုကြာနိုင်လို့ borrowed reference ဟာ dangling ဖြစ်နိုင်ချေရှိလို့ compiler က compile error ဖြင့် ချက်ချင်း block ပေးပါတယ်။
`mpsc::Sender` (`tx`) ကို thread များစွာအတွက် clone မလုပ်ဘဲ owner တစ်ခုတည်းအဖြစ် share ကြိုးစားခြင်း—Sender ကို thread တစ်ခုစီ ကိုယ်ပိုင် copy ရှိဖို့ `tx.clone()` လိုအပ်ပါတယ်—မဟုတ်ရင် ownership move ပြဿနာနှင့် ရင်ဆိုင်ရနိုင်ပါတယ်။