我正在尝试为来自crossterm
机箱的输入创建一个未来的轮询,据我所知,该机箱不提供异步API。
起初,我试着做以下事情:
use crossterm::event::poll as crossterm_poll;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::time::{sleep, timeout};
struct Polled {}
impl Polled {
pub fn new() -> Polled {
Polled {}
}
}
impl Future for Polled {
type Output = bool;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// If there are events pending, it returns "Ok(true)", else it returns instantly
let poll_status = crossterm_poll(Duration::from_secs(0));
if poll_status.is_ok() && poll_status.unwrap() {
return Poll::Ready(true);
}
Poll::Pending
}
}
pub async fn poll(d: Duration) -> Result<bool, ()> {
let polled = Polled::new();
match timeout(d, polled).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
从技术上讲,它是有效的,但不幸的是,程序开始一直使用100%的CPU,因为执行器总是试图轮询未来,以防出现新的情况。因此,我想添加一些异步等效的睡眠,这将推迟执行器下次尝试轮询Future的时间,所以我尝试添加以下内容(就在返回Poll::Pending
之前(,这显然不起作用,因为sleep_future::poll()
只返回Pending
:
let mut sleep_future = sleep(Duration::from_millis(50));
tokio::pin!(sleep_future);
sleep_future.poll(cx);
cx.waker().wake_by_ref();
poll不是async
这一事实禁止使用async
函数,我开始怀疑我想做的事情是否真的可行,或者我是否没有以错误的方式解决我的第一个问题。
找到一种方法让async
睡眠是一个好方法吗?如果不是,那是什么?我是不是在异步范例中遗漏了什么?或者,如果机箱没有为您提供必要的工具,那么有时就不可能将一些同步逻辑封装到Future中吗?
无论如何,提前谢谢!
编辑:我找到了一种使用async
块做我想做的事情的方法:
pub async fn poll(d: Duration) -> Result<bool, ()> {
let mdr = async {
loop {
let a = crossterm_poll(Duration::from_secs(0));
if a.is_ok() && a.unwrap() {
break;
}
sleep(Duration::from_millis(50)).await;
}
true
};
match timeout(d, mdr).await {
Ok(b) => Ok(b),
Err(_) => Err(()),
}
}
这是惯用的方法吗?还是我错过了更优雅的东西?
是的,使用async
块是组合期货的好方法,就像您的自定义轮询器和tokio的sleep
一样。
然而,如果您确实想编写自己的Future
,它也调用tokio的sleep
,以下是您需要做的不同操作:
- 不要立即调用
wake_by_ref()
——当时间到来时,睡眠未来会处理好这一问题,这就是避免旋转(使用100%CPU(的方法 - 当你打算睡觉时(不是每次轮询时(,你必须构造一次
sleep()
future,然后将其存储在你的future中(这需要pin投影(,并在下次轮询时再次轮询同一future。这就是你如何确保你等待预定的时间,而不是更短
异步块通常是获得相同结果的更容易的方法。