在Tokio中模拟时间:调用pause()后sleep为何未阻塞?
Tokio time::pause() 调用后sleep未按预期永久挂起的原因及解决方法
你在测试中调用tokio::time::pause()后执行10秒sleep,预期会永久挂起,但测试却完成并输出接近10秒的耗时,核心原因是**time::pause()的调用时机错误**。
原因分析
Tokio的time::pause()要求在runtime调度器启动前调用才能生效。你使用#[tokio::test]宏时,宏会先自动初始化并启动runtime,再执行测试函数内的代码。当time::pause()在测试函数里执行时,时间调度机制已经处于正常运行状态,此时调用pause不会产生预期的暂停效果,sleep仍会按照真实时间推进。
解决方法
有两种可行的修正方式:
1. 使用#[tokio::test]的start_paused参数(Tokio 1.16+支持)
直接在测试宏中开启启动时暂停时间的配置,无需手动调用time::pause():
use tokio::time; use tokio::time::{Instant, Duration}; #[tokio::test(flavor = "current_thread", start_paused = true)] async fn pause_time() { let i0 = Instant::now(); time::sleep(Duration::from_secs(10)).await; let i1 = Instant::now(); println!("{:?}", i1-i0); // 此时会输出接近0的耗时,时间被暂停后sleep不会等待真实时间 }
2. 手动创建Runtime并在启动前调用time::pause()
通过tokio::runtime::Builder手动构建runtime,确保在启动前调用pause:
use tokio::runtime::Builder; use tokio::time; use tokio::time::{Instant, Duration}; fn main() { let runtime = Builder::new_current_thread() .enable_time() .build() .unwrap(); // 必须在启动runtime前调用pause time::pause(); runtime.block_on(async { let i0 = Instant::now(); time::sleep(Duration::from_secs(10)).await; let i1 = Instant::now(); println!("{:?}", i1-i0); // 输出接近0的耗时 }); }
补充说明
当时间被暂停后,Instant::now()会返回固定值,只有通过time::advance()主动推进时间,或者当runtime没有待执行任务时才会自动推进时间。如果需要让sleep完成,你需要手动调用time::advance(Duration::from_secs(10))来推进时间。
内容的提问来源于stack exchange,提问作者Victor Ermolaev
相关产品推荐
相关产品推荐

