为什么Future::select 会先选择睡眠时间较长的future? [英] Why does Future::select choose the future with a longer sleep period first?

查看:30
本文介绍了为什么Future::select 会先选择睡眠时间较长的future?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试了解 Future::select:本例中,先返回延迟时间较长的future.

I'm trying to understand Future::select: in this example, the future with a longer time delay is returned first.

当我阅读这篇文章 和它的例子,我得到了认知失调.作者写道:

When I read this article with its example, I get cognitive dissonance. The author writes:

select 函数运行两个(或更多,如果是 select_all)期货并返回第一个完成的期货.这对于实现超时很有用.

The select function runs two (or more in case of select_all) futures and returns the first one coming to completion. This is useful for implementing timeouts.

好像不太明白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);
}

我需要处理时间延迟较小的早期未来,然后处理延迟较长的未来.我该怎么做?

I need to process the early future with the smaller time delay, and then work with the future with a longer delay. How can I do it?

推荐答案

TL;DR: use tokio::time

如果有一点需要注意:永远在异步操作中执行阻塞或长时间运行的操作.

TL;DR: use tokio::time

If there's one thing to take away from this: never perform blocking or long-running operations inside of asynchronous operations.

如果您想要超时,请使用 tokio 中的内容::time,比如delay_for超时:

If you want a timeout, use something from tokio::time, such as delay_for or 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"),
    }
}

有什么问题?

要理解为什么你会得到你所做的行为,你必须在高层次上理解期货的实现.

What's the problem?

To understand why you get the behavior you do, you have to understand the implementation of futures at a high level.

当您调用 run 时,有一个循环在传入的 future 上调用 poll.循环直到未来返回成功或失败,否则未来还没有完成.

When you call run, there's a loop that calls poll on the passed-in future. It loops until the future returns success or failure, otherwise the future isn't done yet.

您对 poll 的实现锁定"了这个循环 5 秒钟,因为没有什么可以中断对 sleep 的调用.到睡眠完成时,未来已准备就绪,因此选择了未来.

Your implementation of poll "locks up" this loop for 5 seconds because nothing can break the call to sleep. By the time the sleep is done, the future is ready, thus that future is selected.

异步超时的实现在概念上是通过在每次轮询时检查时钟来实现的,判断是否已经过了足够的时间.

The implementation of an async timeout conceptually works by checking the clock every time it's polled, saying if enough time has passed or not.

最大的不同在于,当​​一个未准备好的未来返回时,可以检查另一个未来.这就是 select 所做的!

The big difference is that when a future returns that it's not ready, another future can be checked. This is what select does!

戏剧性的重演:

基于睡眠的定时器

核心:嘿select,你准备好了吗?

选择:嘿future1,你准备好了吗?

future1:稍等片刻 [... 5 秒过去...] nnnnd.是的!

future1: Hold on a seconnnnnnnn [... 5 seconds pass ...] nnnnd. Yes!

简单的基于异步的计时器

核心:嘿select,你准备好了吗?

选择:嘿future1,你准备好了吗?

future1:检查手表

select:嘿future2,你准备好了吗?

future2:检查手表否.

核心:嘿select,你准备好了吗?

[...投票继续...]

[... polling continues ...]

[... 1 秒过去...]

[... 1 second passes ...]

核心:嘿select,你准备好了吗?

选择:嘿future1,你准备好了吗?

future1:检查手表

select:嘿future2,你准备好了吗?

future2:检查手表是的!

这个简单的实现一遍又一遍地轮询期货,直到它们全部完成.这不是最有效的,也不是大多数执行者所做的.

This simple implementation polls the futures over and over until they are all complete. This is not the most efficient, and not what most executors do.

请参阅如何在不使用任何外部依赖项的情况下执行 async/await 函数?执行者.

基于异步的智能定时器

核心:嘿select,你准备好了吗?

选择:嘿future1,你准备好了吗?

future1:Checks watch 没有,但有变化时我会打电话给你.

future1: Checks watch No, but I'll call you when something changes.

select:嘿future2,你准备好了吗?

future2:Checks watch 没有,但有变化时我会打电话给你.

future2: Checks watch No, but I'll call you when something changes.

[...核心停止轮询...]

[... core stops polling ...]

[... 1 秒过去...]

[... 1 second passes ...]

future2:嘿,核心,有些事情改变了.

future2: Hey core, something changed.

核心:嘿select,你准备好了吗?

选择:嘿future1,你准备好了吗?

future1:检查手表

select:嘿future2,你准备好了吗?

future2:检查手表是的!

这个更有效的实现在轮询时为每个未来提供一个唤醒者.当未来尚未准备好时,它会将唤醒者保存起来以备后用.当事情发生变化时,唤醒者通知执行者的核心现在是重新检查期货的好时机.这允许执行器不执行有效的忙等待.

This more efficient implementation hands a waker to each future when it is polled. When a future is not ready, it saves that waker for later. When something changes, the waker notifies the core of the executor that now would be a good time to re-check the futures. This allows the executor to not perform what is effectively a busy-wait.

当您有一个阻塞或长时间运行的操作时,适当的做法是将该工作移出异步循环.有关详细信息和示例,请参阅 在 future-rs 中封装阻塞 I/O 的最佳方法是什么?.

When you have have an operation that is blocking or long-running, then the appropriate thing to do is to move that work out of the async loop. See What is the best approach to encapsulate blocking I/O in future-rs? for details and examples.

这篇关于为什么Future::select 会先选择睡眠时间较长的future?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆