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

问题描述

我正试图了解 Future::select :在此示例中,将首先返回具有较长时间延迟的未来.

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的含义.

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:使用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 timeout :

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时,会有一个循环在传入的将来调用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!

戏剧性的重演:

基于睡眠的计时器

选择:future1,您准备好了吗?

未来1 :保持seconnnnnnnn [... 5秒过去...] nnnnd.是的!

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

基于异步的简单计时器

选择:future1,您准备好了吗?

未来1 :检查手表

选择:future2,您准备好了吗?

未来2 :检查手表

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

core: Hey select, are you ready to go?

[...继续轮询...]

[... polling continues ...]

[... 1秒通过...]

[... 1 second passes ...]

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

core: Hey select, are you ready to go?

选择:future1,您准备好了吗?

未来1 :检查手表

选择:future2,您准备好了吗?

未来2 :检查手表是!

这个简单的实现反复地对期货进行轮询,直到全部完成.这不是最有效的方法,也不是大多数执行程序所能做的.

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函数?执行者.

基于智能的异步计时器

选择:future1,您准备好了吗?

未来1 :检查手表不会,但是发生变化时我会打电话给您.

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

选择:future2,您准备好了吗?

未来2 :检查手表不会,但是发生变化时我会打电话给您.

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

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

[... core stops polling ...]

[... 1秒通过...]

[... 1 second passes ...]

未来2 :核心,有些变化.

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

core: Hey select, are you ready to go?

选择:future1,您准备好了吗?

未来1 :检查手表

选择:future2,您准备好了吗?

未来2 :检查手表是!

这种更有效的实现使更老的能够在每次轮询时交付给每个未来.如果未来还没有准备好,它将保存该唤醒器以供以后使用.当事情发生变化时,唤醒者会通知执行者的核心,现在是重新检查期货的好时机.这使执行者无法执行实际上是忙等待的事情.

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首先选择睡眠时间更长的未来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-31 18:41