为什么Future::select首先选择睡眠时间较长的未来?

为什么Future::select首先选择睡眠时间较长的未来?,select,rust,future,Select,Rust,Future,我试图理解:在这个例子中,延迟时间较长的未来首先返回 当我阅读它的例子时,我会产生认知失调。作者写道: select函数运行两个(如果是select\u all),并返回即将完成的第一个期货。这对于实现超时非常有用 似乎我不明白select的意思 extern crate futures; // v0.1 (old) extern crate tokio_core; use std::thread; use std::time::Duration; use futures::{Async, F

我试图理解:在这个例子中,延迟时间较长的未来首先返回

当我阅读它的例子时,我会产生认知失调。作者写道:

select
函数运行两个(如果是
select\u all
),并返回即将完成的第一个期货。这对于实现超时非常有用

似乎我不明白
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);
}
extern板条箱期货;//v0.1(旧版)
外部板条箱东京大学核心;
使用std::线程;
使用std::time::Duration;
使用未来:{Async,Future};
使用tokio_堆芯::反应堆::堆芯;
结构超时{
时间:u32,,
}
impl超时{
fn新建(时段:u32)->超时{
超时{time:period}
}
}
impl未来超时{
项目类型=u32;
类型错误=字符串;
fn轮询(&mut self)->结果{
线程::睡眠(持续时间::from_secs(self.time为u64));
println!(“超时是用时间{}完成的,self.time);
Ok(异步::就绪(self.time))
}
}
fn main(){
让mut reactor=Core::new().unwrap();
let time_out 1=超时::新建(5);
let time_out 2=超时::新建(1);
让task=time\u out1.选择(time\u out2);
让mut reactor=Core::new().unwrap();
反应堆运行(任务);
}
我需要以较小的时间延迟处理早期的未来,然后以更长的时间延迟处理未来。我该怎么做?

TL;DR:使用东京:时间 如果有一件事需要做的话:永远不要在异步操作中执行阻塞或长时间运行的操作

如果需要超时,请使用中的某些内容,例如或:

有什么问题吗? 要理解你为什么会有这样的行为,你必须从高层次上理解未来的实现

当您调用
run
时,会有一个循环调用
poll
,对将来传递的数据进行查询。它循环直到未来返回成功或失败,否则未来还没有完成

您的
poll
实现将此循环“锁定”5秒钟,因为没有任何东西可以中断对
sleep
的调用。当睡眠结束的时候,未来已经准备好了,因此未来是被选择的

异步超时的实现在概念上是通过每次轮询时检查时钟来工作的,比如说是否经过了足够的时间

最大的区别是,当一个未来还没有准备好时,另一个未来可以被检查。这就是
select
所做的

戏剧性的重演:

基于睡眠的计时器

核心:嘿
选择
,准备好出发了吗

选择:嘿
未来1
,你准备好出发了吗

未来1:保持秒[5秒通过…]nnnnd。对!

简单的基于异步的计时器

核心:嘿
选择
,准备好出发了吗

选择:嘿
未来1
,你准备好出发了吗

未来1:检查表号

选择:嘿
未来2
,你准备好出发了吗

未来2:检查表号

核心:嘿
选择
,准备好出发了吗

[…投票继续…]

[…1秒通过…]

核心:嘿
选择
,准备好出发了吗

选择:嘿
未来1
,你准备好出发了吗

未来1:检查表号

选择:嘿
未来2
,你准备好出发了吗

未来2:检查手表是

这个简单的实现反复轮询未来,直到它们全部完成。这不是最有效的,也不是大多数遗嘱执行人所做的

有关此类执行器的实现,请参阅

智能异步定时器

核心:嘿
选择
,准备好出发了吗

选择:嘿
未来1
,你准备好出发了吗

未来1:没有,但有什么变化我会打电话给你

选择:嘿
未来2
,你准备好出发了吗

未来2:没有,但有什么变化我会打电话给你

[…核心停止轮询…]

[…1秒通过…]

未来2:嘿,核心,有些事情改变了

核心:嘿
选择
,准备好出发了吗

选择:嘿
未来1
,你准备好出发了吗

未来1:检查表号

选择:嘿
未来2
,你准备好出发了吗

未来2:检查手表是

这种更高效的实现在轮询每个未来时为其提供一个唤醒器。当一个未来还没有准备好时,它会把这个唤醒器保存起来以备将来使用。当某些事情发生变化时,唤醒器通知执行器的核心,现在是重新检查未来的好时机。这允许执行者不执行有效的繁忙等待

通用解决方案
当您有一个阻塞或长时间运行的操作时,适当的做法是将该工作移出异步循环。有关详细信息和示例,请参见。

是否可以使用
CpuPool
中的
CpuPool
使用
futures\u CpuPool::CpuPool
?Sergey我不确定是否理解您的问题,但是我试图回答。基于异步定时器的检查率是否会影响程序的性能?如果是,我如何配置它,如果不是,为什么?@Renkai
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"),
    }
}