You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:41:14