Day 6:async 與並發資料共享(tokio 版)

學習目標

  • 在 async 環境用共享狀態
  • 用 tokio::sync 的型別取代 std::sync
  • 處理不需要 clone 的共享資料

今日重點

std::sync::Mutex 在 async 的陷阱

.await 之間**持有** std::sync::MutexGuard 可能造成問題(不能跨 await 持有。會阻擋其他任務)。async 裡建議用 tokio::sync::Mutex

tokio::sync::Mutex

use tokio::sync::Mutex;
use std::sync::Arc;

struct Counter { value: Mutex<i32> }

#[tokio::main]
async fn main() {
    let counter = Arc::new(Counter { value: Mutex::new(0) });

    let mut handles = vec![];
    for _ in 0..5 {
        let c = Arc::clone(&counter);
        handles.push(tokio::spawn(async move {
            let mut v = c.value.lock().await;
            *v += 1;
        }));
    }
    for h in handles { h.await.unwrap(); }

    println!("count = {}", counter.value.lock().await);
}

lock() 也是 async(.await),因為在 async 環境中等待鎖也要讓出執行權。

broadcast(多訂閱者通道)

use tokio::sync::broadcast;
// tx.subscribe() 可建立多個接收者,適合 pub/sub

練習

use tokio::sync::Mutex;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    let shared = Arc::new(Mutex::new(0i64));
    let mut handles = vec![];

    for _ in 0..4 {
        let shared = Arc::clone(&shared);
        handles.push(tokio::spawn(async move {
            for _ in 0..1000 { *shared.lock().await += 1; }
        }));
    }
    for h in handles { h.await.unwrap(); }
    println!("{}", *shared.lock().await);   // 4000
}
2. 想想:為什麼這個例子用 tokio::sync::Mutex 而不是 std。

自我檢查

  • 知道 async 中 Mutex 的注意事項
  • 會用 tokio::sync::Mutex
  • 理解為什麼 lock() 在 tokio 是 async
  • 知道 broadcast 通道的存在

深入連結

  • tokio 官方文件(Synchronization, Broadcast)