Rust : futures::future::join_all(...).await 按顺序运行 future,但需要并行性

nor*_*bjd 7 asynchronous rust async-await

我有以下async功能(实现并不重要):

async fn long_task(task_number: i32) {
    // do some long work
    println!("Task {} executed", task_number);
}
Run Code Online (Sandbox Code Playgroud)

我想同时运行n次这个函数,所以我定义了这个函数:

async fn execute_long_tasks_async(n: i32) {
    let mut futures = Vec::new();
    for i in 1..=n {
        futures.push(long_task(i));
    }
    futures::future::join_all(futures).await;
}
Run Code Online (Sandbox Code Playgroud)

我正在使用该join_all函数来等待所有任务都执行完毕。然后我在我的中调用这个函数main

fn main() {
    futures::executor::block_on(execute_long_tasks_async(3));
}
Run Code Online (Sandbox Code Playgroud)

我的问题是任务是按顺序运行的:

Executing task 1
Task 1 executed
Executing task 2
Task 2 executed
Executing task 3
Task 3 executed
Run Code Online (Sandbox Code Playgroud)

但我希望它同时运行,我会得到类似的结果:

Executing task 1
Executing task 3
Executing task 2
Task 1 executed
Task 3 executed
Task 2 executed
Run Code Online (Sandbox Code Playgroud)

是否有替代方法可以futures::future::join_all并行运行所有任务?

我想用来await创建一个简单的示例来演示asyncawait

Mat*_*247 14

join_all同时运行任务(不是并行)。它有一个限制,即只能在任务让步时才能在任务之间切换。此外,如果第一个任务还没有准备好,它总是更愿意执行第一个任务。

如果你的函数例如定义为

async fn long_task(task_number: i32) {
    println!("Executing Task {}", task_number);
    tokio::time::delay_for(Duration::from_secs(1)).await;
    println!("Task {} executed", task_number);
}
Run Code Online (Sandbox Code Playgroud)

那么await函数中间的 / 挂起点将提供join_all运行其他函数的机会Future- 并且您将观察到预期的输出。

但是,如果long_task不产生(例如,因为它将是线程阻塞),则该函数将在其他任务开始之前运行完成:

async fn long_task(task_number: i32) {
    println!("Executing Task {}", task_number);
    std::thread::sleep(Duration::from_secs(1));
    println!("Task {} executed", task_number);
}
Run Code Online (Sandbox Code Playgroud)

如果你有这样的功能,那么它们通常不太适合这个async世界。async函数的目的不是占用 CPU 资源,也不应该阻塞线程 - 以便同一执行器上调度的其他函数仍然可以运行。

一种可能为您提供更多并行性的替代方案是使用多线程异步运行时(例如 tokio)并将spawn每个异步函数作为单独的任务。在这种情况下,任务可以在单独的 CPU 核心和线程上运行,并且不会相互阻塞太多。然后,您可以使用join_all返回的集合JoinHandle来等待所有任务完成。