我试图理解 Future::select
:在此示例中,将首先返回具有较长时间延迟的将来。
当我阅读this article及其示例时,我得到了认知失调。作者写道:
看来我不了解select
的含义。
extern crate futures; // v0.1 (old)
extern crate tokio_core;
use std::thread;
use std::time::Duration;
use futures::{Async, Future};
use tokio_core::reactor::Core;
struct Timeout {
time: u32,
}
impl Timeout {
fn new(period: u32) -> Timeout {
Timeout { time: period }
}
}
impl Future for Timeout {
type Item = u32;
type Error = String;
fn poll(&mut self) -> Result<Async<u32>, Self::Error> {
thread::sleep(Duration::from_secs(self.time as u64));
println!("Timeout is done with time {}.", self.time);
Ok(Async::Ready(self.time))
}
}
fn main() {
let mut reactor = Core::new().unwrap();
let time_out1 = Timeout::new(5);
let time_out2 = Timeout::new(1);
let task = time_out1.select(time_out2);
let mut reactor = Core::new().unwrap();
reactor.run(task);
}
我需要以较小的延迟处理早期的将来,然后以较长的延迟处理 future 。我该怎么做?
最佳答案
TL; DR:使用tokio::time
如果要解决的事情是一件事:绝对不要在异步操作内执行阻塞或长时间运行的操作。
如果要超时,请使用 tokio::time
中的某些内容,例如 delay_for
或 timeout
:
use futures::future::{self, Either}; // 0.3.1
use std::time::Duration;
use tokio::time; // 0.2.9
#[tokio::main]
async fn main() {
let time_out1 = time::delay_for(Duration::from_secs(5));
let time_out2 = time::delay_for(Duration::from_secs(1));
match future::select(time_out1, time_out2).await {
Either::Left(_) => println!("Timer 1 finished"),
Either::Right(_) => println!("Timer 2 finished"),
}
}
有什么问题?
要了解为什么会表现出自己的行为,您必须全面了解 future 的实现情况。
调用
run
时,有一个循环在传入的将来调用poll
。它一直循环直到将来返回成功或失败,否则将来还没有完成。您的
poll
实现“锁定”此循环5秒钟,因为没有任何东西可以中断对sleep
的调用。到 sleep 结束时, future 就准备好了,因此就选择了 future 。从概念上讲,异步超时的实现是通过在每次轮询时检查时钟,即是否经过了足够的时间来检查时钟的。
最大的区别是,当将来返回尚未准备就绪时,可以检查另一个将来。这就是
select
所做的!戏剧性的重演:
基于 sleep 的计时器
基于异步的简单计时器
这个简单的实现会一遍又一遍地轮询 future ,直到全部完成。这不是最有效的方法,也不是大多数执行程序所执行的操作。
有关此类执行程序的实现,请参见How do I execute an async/await function without using any external dependencies?。
基于智能异步的计时器
这种更有效的实现方式在轮询每个将来时将唤醒者。如果 future 还没有准备好,它将保存该唤醒器以供以后使用。当情况发生变化时,唤醒者会通知执行者的核心,现在是重新检查 future 的好时机。这使执行者不能执行实际上是忙等待的事情。
通用解决方案
当您的某个操作被阻塞或长时间运行时,那么适当的事情是将工作移出异步循环。有关详细信息和示例,请参见What is the best approach to encapsulate blocking I/O in future-rs?。
关于select - 为什么选择Future::select首先选择 sleep 时间更长的 future ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48735952/