我如何在不等待的情况下运行未来?(生锈)

Ser*_*rrr 5 rust rust-tokio rust-async-std

我有一些异步函数

async fn get_player(name: String, i: Instant) -> Option<Player> {
// some code here that returns a player structs
}
Run Code Online (Sandbox Code Playgroud)

在我的主函数中,我想在循环中同时运行上述函数,该函数大约需要 1 秒才能完成,并且我需要运行它至少 50 次,因此我想让它同时运行该函数 50 次。在我的主函数中,我有一个lazy_static自定义Client结构,该结构不应创建多次。

主功能

#[tokio::main]
async fn main() {
    client.init().await;

    println!("start");
    for i in 0..10 {
        println!("{}", i);
        let now = Instant::now();

        tokio::spawn(async move  {
            client.get_player("jay".to_string(), now).await;
        });
    }
    loop {}
}
Run Code Online (Sandbox Code Playgroud)

我传递即时的原因是因为在我的 get_player 函数中我有一个 println!() 只打印执行时间。

上面的 main 方法每个函数调用大约需要 500ms,但是下面的代码只需要 100ms。

#[tokio::main]
async fn maain(){
    client.init().await;

    for i in 0..10 {
        let now = Instant::now();
        client.get_player("jay".to_string(), now).await.expect("panic");
    }
}
Run Code Online (Sandbox Code Playgroud)

但这个函数仍然是同步代码,我如何在没有时间成本的情况下实际并发运行异步函数?

  • 为了更好地理解我所追求的是与此类似的实现(顺便说一句,它在 java 中),
     CompleteableFuture.thenAccept(x -> x.SayHello(););
Run Code Online (Sandbox Code Playgroud)

或者在 Js 中它类似于.then异步函数之后。

Rust中有类似的实现吗?

Fin*_*nis 7

我假设您的get_player函数需要一秒钟,因为它等待网络交互,而不是因为某些计算需要那么长时间。如果它是受计算限制的,那么异步是错误的方法,您应该改用并行。

此外,我假设 的 函数签名get_player实际上是async fn get_player(&self, name: String, i: Instant) -> Option<Player>相反,因为否则您的main代码示例都没有任何意义。尽管我很困惑为什么会是&self而不是&mut self

有了这些假设,我尝试重现您的最小可重现示例:

use std::time::{Duration, Instant};

#[derive(Debug)]
struct Player {
    name: String,
}

struct Client {}

impl Client {
    async fn init(&self) {}

    async fn get_player(&self, name: String, _now: Instant) -> Option<Player> {
        // Dummy code that simulates a delay of 1 second
        tokio::time::sleep(Duration::from_millis(1000)).await;
        Some(Player { name })
    }
}

static client: Client = Client {};

#[tokio::main]
async fn main() {
    let begin = Instant::now();
    client.init().await;

    for i in 0..10 {
        let now = Instant::now();
        let player = client
            .get_player(format!("Player #{}", i), now)
            .await
            .expect("panic");
        println!(
            "[{} ms] Retreived player: {:?}",
            begin.elapsed().as_millis(),
            player.name
        );
    }
}
Run Code Online (Sandbox Code Playgroud)
use std::time::{Duration, Instant};

#[derive(Debug)]
struct Player {
    name: String,
}

struct Client {}

impl Client {
    async fn init(&self) {}

    async fn get_player(&self, name: String, _now: Instant) -> Option<Player> {
        // Dummy code that simulates a delay of 1 second
        tokio::time::sleep(Duration::from_millis(1000)).await;
        Some(Player { name })
    }
}

static client: Client = Client {};

#[tokio::main]
async fn main() {
    let begin = Instant::now();
    client.init().await;

    for i in 0..10 {
        let now = Instant::now();
        let player = client
            .get_player(format!("Player #{}", i), now)
            .await
            .expect("panic");
        println!(
            "[{} ms] Retreived player: {:?}",
            begin.elapsed().as_millis(),
            player.name
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

这是基于你的最后一个main例子。正如你所看到的,检索所有玩家需要 10 秒,因为他们都是按顺序运行的。

现在让我们异步运行它们。这里的问题是同时将它们全部加入。遗憾的是,Tokio 并没有提供一种简单的方法。你可以tokio::spawn把它们全部收集起来JoinHandle,然后一一加入。然而,板条箱futures提供的正是您想要的:

use std::time::{Duration, Instant};

#[derive(Debug)]
struct Player {
    name: String,
}

struct Client {}

impl Client {
    async fn init(&self) {}

    async fn get_player(&self, name: String, _now: Instant) -> Option<Player> {
        // Dummy code her that simulates a delay of 1 second
        tokio::time::sleep(Duration::from_millis(1000)).await;
        Some(Player { name })
    }
}

static client: Client = Client {};

#[tokio::main]
async fn main() {
    let begin = Instant::now();
    client.init().await;

    let get_player_futures = (0..10).into_iter().map(|i| async move {
        let now = Instant::now();
        let player = client
            .get_player(format!("Player #{}", i), now)
            .await
            .expect("panic");
        println!(
            "[{} ms] Retreived player: {:?}",
            begin.elapsed().as_millis(),
            player.name
        );
    });

    futures::future::join_all(get_player_futures).await;
}
Run Code Online (Sandbox Code Playgroud)
[1002 ms] Retreived player: "Player #0"
[2004 ms] Retreived player: "Player #1"
[3005 ms] Retreived player: "Player #2"
[4008 ms] Retreived player: "Player #3"
[5010 ms] Retreived player: "Player #4"
[6011 ms] Retreived player: "Player #5"
[7013 ms] Retreived player: "Player #6"
[8014 ms] Retreived player: "Player #7"
[9016 ms] Retreived player: "Player #8"
[10018 ms] Retreived player: "Player #9"
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,整个程序只花了一秒钟,而且所有的数据都被同时检索到。

get_player_futures这是一个遍历所有需要等待才能检索玩家的 future 的迭代器。futures::future::join_all然后同时等待他们所有人。您甚至可以使用join_all的返回值来检索 future 的值,但我们在这里不使用它。

我希望这能有所帮助;由于您的问题的某些部分不连贯,因此很难创建答案。