0Pricing
Learn Rust Coding · บทเรียน

ย้ายข้อมูลเข้าเธรด

ใช้คลอสเชอร์แบบ 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. สร้างเธรด
  2. ย้ายข้อมูลเข้าเธรด
  3. ใช้ข้อมูลร่วมกับ Arc และ Mutex
  4. รอเธรดและรวบรวมผลลัพธ์
← กลับไปที่ Learn Rust Coding