如何在Future::poll()中正确使用sleep与wake?避免忙循环
解决Future::poll()中sleep导致忙循环的问题
你期望在Future::poll()中加入sleep避免忙循环,让Runtime每秒调用一次poll(),第5秒时返回Poll::Ready(()),但当前代码中cx.waker().wake_by_ref()会触发任务立即重新执行,导致CPU忙循环,输出大量"1"而非预期的5个。
问题根源
- 手动调用
cx.waker().wake_by_ref()会直接唤醒任务,完全跳过了TokioSleepfuture的定时器等待逻辑,导致poll被无间断调用,形成忙循环。 - 现有
Delay结构中的sleeper是固定的1秒sleep,没有在每次等待完成后重置,即使去掉手动唤醒,也只会等待一次1秒,后续poll无法触发新的定时等待。
修正方案
去掉手动唤醒逻辑,让Sleep future自行管理定时唤醒;同时每次sleeper完成等待后,重置它为新的1秒sleep,并输出"1",直到目标时间到达。
修正后的完整代码:
use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; use pin_project_lite::pin_project; use std::time::{Duration, Instant}; pin_project! { struct Delay { when: Instant, #[pin] sleeper: tokio::time::Sleep, } } impl Delay { pub fn new(when: Instant) -> Self { Self { when, sleeper: tokio::time::sleep(Duration::from_secs(1)) } } } impl Future for Delay { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { // 先检查是否到达目标时间 if self.when < Instant::now() { return Poll::Ready(()); } let mut this = self.project(); // 检查当前sleeper是否完成 match this.sleeper.as_mut().poll(cx) { Poll::Ready(()) => { // 1秒到了,输出"1" print!("1"); // 重置sleeper为下一个1秒的sleep *this.sleeper = tokio::time::sleep(Duration::from_secs(1)); // 返回Pending,等待下一次定时唤醒 Poll::Pending } Poll::Pending => { // 还没到1秒,继续等待 Poll::Pending } } } } #[tokio::main] async fn main() { let when = Instant::now() + Duration::from_secs(5); let delay1 = Delay::new(when); delay1.await; println!("Hello, world!"); }
代码说明
- 每次poll时先判断是否到达目标时间,到达则返回
Poll::Ready(())结束future。 - 若未到达目标时间,poll当前
sleeper:如果返回Ready,说明1秒已过,输出"1"并重置sleeper为新的1秒定时;如果返回Pending,则让任务继续等待定时器唤醒。 - 完全依赖Tokio的
Sleepfuture管理定时唤醒,避免了手动唤醒导致的忙循环,同时保证每秒触发一次poll,输出一个"1",5秒后完成任务。
内容的提问来源于stack exchange,提问作者Anunaki
相关产品推荐
相关产品推荐

