异步代码里该用 std::sync::Mutex 还是 tokio::sync::Mutex?答案不是「异步就用异步的锁」。这篇用编译错误、真实死锁和 400 万次加锁的计时,说清楚两者各自的边界。
error: future cannot be sent between threads safely
--> src/main.rs:5:5
|
5 | tokio::spawn(async move { let mut g = m.lock().unwrap(); *g += 1; tokio::time::sleep(std::time::Duration::from_millis(1)).await; }).aw...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `{async block@src/main.rs:5:18: 5:28}`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, i32>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:5:127
|
5 | ...c move { let mut g = m.lock().unwrap(); *g += 1; tokio::time::sleep(std::time::Duration::from_millis(1)).await; }).await.unwrap();
| ----- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` ^^^^^ await occurs here, with `mut g` maybe used later
note: required by a bound in `tokio::spawn`
--> <实验目录>/cargo_home/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.53.1/src/task/spawn.rs:176:21Send 的规则见Send 与 Sync;spawn 的约束见spawn 与 'static。
编译退出码 0 4 个任务各自 持锁 → sleep 20 ms(跨 await)→ +1,最终值 4(没有丢更新)
编译退出码 0 std Mutex 只在块内持有,任务可以 spawn:[3, 2, 1, 0]
编译退出码 0 [] 6 秒内程序没有退出:B 阻塞了唯一的线程,连 500 ms 的 timeout 也没有机会触发 → 死锁
阻塞线程的更多后果见阻塞代码混入异步任务。
编译退出码 0 std::sync::Mutex 4 任务 × 100 万次: 75.5 ms tokio::sync::Mutex 4 任务 × 100 万次: 389.7 ms
这是 macOS aarch64 上的一次微基准,只测了高争用的短临界区。