Day 5:共享狀態與 Mutex

學習目標

  • 用 Mutex 保護共享資料
  • 用 Arc 讓多執行緒共享所有權
  • 避免死鎖與資料競爭

今日重點

Mutex:互斥鎖

use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);

    {
        let mut num = m.lock().unwrap();   // 取得鎖
        *num = 6;                          // 修改受保護資料
    }   // 離開 scope 自動釋放鎖

    println!("m = {:?}", m);   // Mutex { data: 6 }
}

lock() 回傳 MutexGuard,Drop 時自動解鎖。透過 * 解引用修改裡面的值。

Arc:原子引用計數

Rc 不能跨執行緒(非 Send)。跨執行緒共享所有權要用 Arc(Atomic Rc)。

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);   // 增加計數
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());   // 10
}

練習

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // 讓 4 個執行緒各把總和加 1000
    let total = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..4 {
        let total = Arc::clone(&total);
        handles.push(thread::spawn(move || {
            for _ in 0..1000 {
                *total.lock().unwrap() += 1;
            }
        }));
    }
    for h in handles { h.join().unwrap(); }
    println!("total = {}", *total.lock().unwrap());   // 4000
}

自我檢查

  • 會用 Mutex 保護資料
  • 知道 lock() 回傳 guard、自動解鎖
  • 知道為什麼跨執行緒用 Arc 而不用 Rc
  • 理解 Mutex 防止資料競爭的原理
  • 了解死鎖風險(lock 沒 drop)

深入連結

  • The Book 第 16 章「Shared-State Concurrency」