Rust中Tokio异步编程行为疑问及正确用法咨询
问题解答
1. 这是正确的行为吗?
是正确的,但和你预期的偏差来自两个关键细节:
写锁被长时间持有
你的ww函数中,写锁在整个sleep期间都没有释放:
async fn ww(w: Arc<RwLock<MyThing>>) { loop { let mut write = w.write().await; // 获取写锁 (*write).n += 2; println!("Updated value: {}", (*write).n); sleep(Duration::from_millis(200)).await; // 休眠时写锁仍被持有 } // 循环结束时`write`才会被销毁,释放写锁 }
Tokio的RwLock写锁是排他性的——只要写锁被持有,所有读锁请求都会阻塞。所以在ww休眠的200ms里,rr一直卡在v.read().await处无法执行。只有当ww循环结束、写锁释放后,rr才能拿到读锁打印一次,随后ww立刻重新获取写锁,导致两者交替输出。
如果想让rr在ww休眠时大量执行,需要提前释放写锁,把sleep移到写锁的作用域外:
async fn ww(w: Arc<RwLock<MyThing>>) { loop { { let mut write = w.write().await; // 缩小写锁作用域 (*write).n += 2; println!("Updated value: {}", (*write).n); } // 此处`write`被销毁,释放写锁 sleep(Duration::from_millis(200)).await; // 休眠时不持有锁 } }
修改后,ww休眠期间写锁处于释放状态,rr可以持续获取读锁打印,符合你的预期。
异步与多线程的核心差异
多线程是操作系统抢占式调度,而异步任务是协作式调度——只有任务遇到await时才会主动让出执行权。你的rr函数没有await(注释了sleep),一旦拿到读锁会连续执行到下一个await点,但因为写锁长时间被持有的问题,这个场景没被触发。
2. 删除tokio::spawn中的await会导致函数不执行吗?
是的,完全不会执行。
原因是:async函数调用后仅返回一个Future对象,它本身不会自动执行,必须被调度器poll才能推进逻辑。
当你写:
tokio::spawn(async move { ww(w).await // await会驱动ww的Future执行 });
await会告诉Tokio调度器需要等待这个Future完成,期间可以调度其他任务。但如果删掉await:
tokio::spawn(async move { ww(w) // 仅返回Future,没有驱动它执行 });
spawn的任务会立刻完成(闭包返回的Future无需做任何操作),ww(w)返回的Future从未被poll,所以ww函数内的逻辑一行都不会执行,rr同理。
内容的提问来源于stack exchange,提问作者MrZlo
相关产品推荐
相关产品推荐

