Что такое фьючерс :: будущее :: ленивый? - PullRequest
0 голосов
/ 11 июня 2019

В документации Tokio у нас есть этот фрагмент:

extern crate tokio;
extern crate futures;

use futures::future::lazy;

tokio::run(lazy(|| {
    for i in 0..4 {
        tokio::spawn(lazy(move || {
            println!("Hello from task {}", i);
            Ok(())
        }));
    }

    Ok(())
}));

Объяснение этому:

Функция lazy запускает замыкание при первом опросе будущего. Это используется здесь, чтобы гарантировать, что tokio::spawn вызывается из задачи. Без lazy, tokio::spawn будет вызван вне контекста задачи, что приведет к ошибке.

Я не уверен, что понимаю это точно, несмотря на некоторое знакомство с Токио. Кажется, что эти два lazy играют несколько разные роли, и что это объяснение применимо только к первому . Разве второй вызов lazy (внутри цикла for) не для того, чтобы преобразовать замыкание в будущее?

1 Ответ

4 голосов
/ 11 июня 2019

цель для ленивых покрыта документацией для lazy:

Создает новое будущее, которое в конечном итоге будет таким же, как и то, которое создано с помощью предоставленного закрытия.

Предоставленное закрытие запускается только тогда, когда на будущее запланирован обратный вызов, в противном случае обратный вызов никогда не выполняется. Однако после запуска это будущее совпадает с будущим, создаваемым закрытием.

Как простое замыкание, оно используется для предотвращения нетерпеливой оценки кода. В синхронных терминах это разница между вызовом функции и вызовом замыкания, которое вернула функция:

fn sync() -> impl FnOnce() {
    println!("This is run when the function is called");

    || println!("This is run when the return value is called")
}

fn main() {
    let a = sync();
    println!("Called the function");
    a();
}

И параллель для фьючерсов 0,1:

use futures::{future, Future}; // 0.1.27

fn not_sync() -> impl Future<Item = (), Error = ()> {
    println!("This is run when the function is called");

    future::lazy(|| {
        println!("This is run when the return value is called");
        Ok(())
    })
}

fn main() {
    let a = not_sync();
    println!("Called the function");
    a.wait().unwrap();
}

С синтаксисом async / await эта функция больше не нужна:

#![feature(async_await)] // 1.37.0-nightly (2019-06-05)

use futures::executor; // 0.3.0-alpha.16
use std::future::Future;

fn not_sync() -> impl Future<Output = ()> {
    println!("This is run when the function is called");

    async {
        println!("This is run when the return value is called");
    }
}

fn main() {
    let a = not_sync();
    println!("Called the function");
    executor::block_on(a);
}

Как вы определили, примеры Tokio используют lazy для:

  • убедитесь, что код в замыкании выполняется только изнутри исполнителя.
  • гарантирует, что замыкание будет выполнено как будущее

Я считаю, что эти два аспекта lazy фактически одинаковы.

Смотри также:

...