如何使用async_std::task::sleep模拟阻塞操作?

How to use async_std::task::sleep to simulate blocking operation?

我有一个像这样的简单代码来模拟异步代码如何处理阻塞操作。

我希望所有这些“你好”打印将在 1000 毫秒后显示。

但是这段代码像普通的阻塞代码一样工作,每个 hello_wait 调用等待 1000 毫秒并在 1000 毫秒后打印另一个 Hello。

我怎样才能运行并发?

use std::{time::Duration};
use async_std::task;

async fn hello_wait(){
    task::sleep(Duration::from_millis(1000)).await;
    println!("Hello");
}

#[async_std::main]
async fn main() {
    hello_wait().await;
    hello_wait().await;
    hello_wait().await;
    hello_wait().await;
    hello_wait().await;
}

这是正在发生的事情:

// -- Wait 1000ms --
Hello
// -- Wait 1000ms --
Hello
// -- Wait 1000ms --
Hello
// -- Wait 1000ms --
Hello
// -- Wait 1000ms --
Hello

这就是我想要的:

// -- Wait 1000ms --
Hello
Hello
Hello
Hello
Hello

How can I make it run concurrently?

您可以:

您的期望可能来自 Javascript 或 C# async,其中可等待的“基础”是 任务。任务是“活动的”,一旦您创建它们,它们就可以被安排并同时做它们的事情。

但是 Rust 的核心 awaitable 更像是一个 coroutine,所以它是 inert (/ passive):创建一个并不能真正做到任何事情,futures 都必须被轮询才能取得进展,await 重复轮询直到完成 才能继续。因此,当您 await 某些东西时,它会完全运行,此时没有机会交错。

因此 运行 期货同时需要两件事之一:

  • 将它们升级为任务,这意味着它们可以自行安排,这就是 spawn 所做的
  • 或者将未来组合成一个单一的“元未来”,它可以在轮询时轮流轮询它们,这就是像 join_all or tokio::join 这样的结构

请注意,组合 futures 不允许 并行性 ,因为 futures 是“兄弟姐妹”,它们只能连续轮询(因此实际上做事),只是这个轮询(以及进度)交织在一起。

生成任务确实允许并行(如果运行时是多线程的并且机器有多个内核——尽管后者现在非常普遍),但在生命周期和内存管理方面有其自身的局限性,并且就是贵了点。

Here is a playground demo of various options。它使用 tokio 因为显然 playground 没有 async_std,而且我不确定是否有可能启用“不稳定”功能,但除此之外还有 tokio::join 的使用(async_std 的 Future::join 一次只能加入 2 个期货,所以你必须链接调用)它应该在 async_std.

中大致相同

我可以用 futures 板条箱的 join_all:

这样做
use std::time::Duration;
use async_std::task;
use futures::future;

async fn hello_wait(){
    task::sleep(Duration::from_millis(1000)).await;
    println!("Hello");
}

#[async_std::main]
async fn main() {
    let mut asyncfuncs = vec![];
    asyncfuncs.push(hello_wait());
    asyncfuncs.push(hello_wait());
    asyncfuncs.push(hello_wait());
    asyncfuncs.push(hello_wait());
    asyncfuncs.push(hello_wait());

    future::join_all(asyncfuncs.into_iter()).await;
}