ย้ายข้อมูลเข้าเธรด
ใช้คลอสเชอร์แบบ move อย่างถูกต้อง
ย้ายข้อมูลเข้าเธรด เป็นบทเรียน Learn Rust Coding ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Learn Rust Coding และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คลอเชอร์จับสภาพแวดล้อม
คลอเชอร์ที่ส่งให้ thread::spawn สามารถใช้ตัวแปรจากขอบเขตโดยรอบได้ โดยค่าเริ่มต้น Rust จะยืมตัวแปรเหล่านั้น
แต่เธรดที่สร้างขึ้นอาจมีอายุยืนกว่าฟังก์ชันที่สร้างเธรด ดังนั้นการยืมแบบปกติจึงไม่ปลอดภัย Rust จะปฏิเสธกรณีนี้ตั้งแต่เวลาคอมไพล์
ปัญหาการยืม
หากเธรดยืมตัวแปรเฉพาะที่ คอมไพเลอร์จะพิสูจน์ไม่ได้ว่าตัวแปรนั้นมีอายุนานพอ เธรดอาจทำงานต่อหลังจากตัวแปรถูกทำลายแล้ว
โค้ดด้านล่างคอมไพล์ไม่ได้ เพราะคลอเชอร์ยืมเพียง data
use std::thread;
fn main() {
let data = vec![1, 2, 3];
// ERROR: closure may outlive `data`
let h = thread::spawn(|| {
println!("{:?}", data);
});
h.join().unwrap();
}คีย์เวิร์ด move
การเพิ่ม move ไว้หน้าคลอเชอร์จะบังคับให้คลอเชอร์รับสิทธิ์ความเป็นเจ้าของตัวแปรที่จับไว้ ตัวแปรเหล่านั้นจะถูกย้ายเข้าไปในเธรด
ตอนนี้เธรดเป็นเจ้าของข้อมูล จึงรับรองได้ว่าข้อมูลจะยังใช้งานได้ตลอดช่วงอายุของเธรด
use std::thread;
fn main() {
let data = vec![1, 2, 3];
let h = thread::spawn(move || {
println!("{:?}", data);
});
h.join().unwrap();
}สิทธิ์ความเป็นเจ้าของถูกโอนออกไป
เมื่อค่าถูกย้ายเข้าไปในเธรดแล้ว ขอบเขตเดิมจะไม่สามารถใช้ค่านั้นได้อีก สิทธิ์ความเป็นเจ้าของถูกโอนไปแล้ว
การพยายามใช้ data ใน main หลังการย้ายจะทำให้เกิดข้อผิดพลาดขณะคอมไพล์ ตอนนี้เธรดเป็นเจ้าของเพียงผู้เดียว
use std::thread;
fn main() {
let data = vec![1, 2, 3];
let h = thread::spawn(move || println!("{:?}", data));
// println!("{:?}", data); // ERROR: value moved
h.join().unwrap();
}การย้ายชนิด Copy
ชนิดที่ใช้งาน Copy ได้ เช่น จำนวนเต็ม จะถูกคัดลอกแทนการย้าย คลอเชอร์จะได้รับสำเนาของตัวเอง
ดังนั้นหลังจากคลอเชอร์แบบ move จับตัวเลขไว้ คุณยังสามารถใช้ค่าต้นฉบับใน main ได้
use std::thread;
fn main() {
let n = 42;
let h = thread::spawn(move || {
println!("thread sees {}", n);
});
println!("main still sees {}", n);
h.join().unwrap();
}Send: ปลอดภัยต่อการโอนย้าย
ชนิดหนึ่งจะย้ายเข้าไปในเธรดอื่นได้ก็ต่อเมื่อใช้งานเทรต Send ได้ Send หมายความว่าปลอดภัยที่จะโอนสิทธิ์ความเป็นเจ้าของข้ามเธรด
ชนิดส่วนใหญ่ใช้งาน Send ได้โดยอัตโนมัติ แต่บางชนิด เช่น Rc ใช้งานไม่ได้ และคอมไพเลอร์จะปฏิเสธการย้ายชนิดเหล่านั้น
Rc ไม่ใช่ Send
Rc เป็นตัวนับการอ้างอิงสำหรับเธรดเดียว ค่าตัวนับของมันไม่ได้รับการป้องกันจากการอัปเดตพร้อมกัน จึงไม่ใช่ Send
การพยายามย้าย Rc เข้าไปในเธรดจะคอมไพล์ไม่ผ่าน ในบทเรียนถัดไปคุณจะใช้ Arc แทน
use std::rc::Rc;
use std::thread;
fn main() {
let r = Rc::new(5);
// ERROR: `Rc<i32>` cannot be sent between threads safely
let h = thread::spawn(move || println!("{}", r));
h.join().unwrap();
}การย้ายค่าหลายค่า
คลอเชอร์แบบ move เดียวสามารถจับตัวแปรหลายตัวพร้อมกันได้ ตัวแปรทั้งหมดจะถูกย้ายเข้าไปในเธรด
วิธีนี้มีประโยชน์เมื่อผู้ปฏิบัติงานต้องใช้ทั้งข้อมูลนำเข้าและป้ายกำกับ
use std::thread;
fn main() {
let label = String::from("sum");
let nums = vec![1, 2, 3, 4];
let h = thread::spawn(move || {
let total: i32 = nums.iter().sum();
println!("{} = {}", label, total);
});
h.join().unwrap();
}การโคลนก่อนย้าย
หากทั้งเธรดและฟังก์ชันหลักต้องใช้ค่าเดียวกัน ให้โคลนค่านั้นก่อน มอบสำเนาหนึ่งให้เธรดและเก็บอีกสำเนาไว้
การโคลนจะคัดลอกข้อมูล ทำให้แต่ละฝั่งเป็นเจ้าของค่าอิสระของตนเอง
use std::thread;
fn main() {
let original = String::from("hello");
let for_thread = original.clone();
let h = thread::spawn(move || println!("thread: {}", for_thread));
println!("main: {}", original);
h.join().unwrap();
}การคืนข้อมูลที่ย้ายกลับมา
เธรดที่เป็นเจ้าของข้อมูลซึ่งถูกย้ายมา สามารถคืนข้อมูลนั้นกลับไป โดยโอนสิทธิ์ความเป็นเจ้าของกลับไปยังเธรดแม่ผ่าน join
รูปแบบนี้จะย้ายข้อมูลเข้าไป ประมวลผล แล้วจึงย้ายผลลัพธ์ออกมา
use std::thread;
fn main() {
let mut v = vec![3, 1, 2];
let h = thread::spawn(move || {
v.sort();
v
});
let sorted = h.join().unwrap();
println!("{:?}", sorted);
}เหตุผลที่ต้องใช้ move
หากไม่มี move ตัวตรวจสอบการยืมจะถือว่าคลอเชอร์กำลังยืมข้อมูล เนื่องจากเธรดที่สร้างขึ้นไม่มีขอบเขตช่วงอายุที่แน่นอนซึ่งผูกกับผู้เรียก การยืมจึงไม่ปลอดภัย
คีย์เวิร์ด move เปลี่ยนการยืมให้เป็นการโอนสิทธิ์ความเป็นเจ้าของ ทำให้เป็นไปตามข้อกำหนด 'static ของ spawn
ตรวจสอบความเข้าใจ
ทดสอบความเข้าใจของคุณเกี่ยวกับการย้ายข้อมูลเข้าเธรด
สรุปทบทวน
คุณได้เรียนรู้แล้วว่าโคลเชอร์ของเธรดต้องเป็นเจ้าของข้อมูลที่ใช้งาน โดยทำได้ด้วยคีย์เวิร์ด move
การย้ายจะโอนความเป็นเจ้าของ ส่วนชนิดข้อมูล Copy จะถูกคัดลอก เทรต Send ใช้ระบุว่าสิ่งใดปลอดภัยที่จะโอนย้าย และ Rc ไม่ใช่ Send
ต่อไปคุณจะได้แชร์ข้อมูลระหว่างเธรดโดยใช้ Arc และ Mutex
คำถามที่พบบ่อย
บทเรียน “ย้ายข้อมูลเข้าเธรด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ย้ายข้อมูลเข้าเธรด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Learn Rust Coding ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Learn Rust Coding มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ย้ายข้อมูลเข้าเธรด”
ใช้คลอสเชอร์แบบ move อย่างถูกต้อง คุณปฏิบัติ Learn Rust Coding ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Learn Rust Coding หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Learn Rust Coding บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “ย้ายข้อมูลเข้าเธรด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Learn Rust Coding นี้ได้ไหม
ได้ บทเรียน Learn Rust Coding ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- สร้างเธรด
- ย้ายข้อมูลเข้าเธรด
- ใช้ข้อมูลร่วมกับ Arc และ Mutex
- รอเธรดและรวบรวมผลลัพธ์