Skip to content

异步计算:AsyncComputeTaskPool

当后台任务可能跨越多帧时,用 AsyncComputeTaskPool。典型场景:

  • 地形生成(噪声计算量大)
  • 寻路(A* 搜索空间大)
  • 资源解压 / 解码

任务提交后,每帧用 check_ready 轮询是否完成:

rust
use bevy::prelude::*;
use bevy::tasks::AsyncComputeTaskPool;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, spawn_task)
        .add_systems(Update, poll_task)
        .run();
}

#[derive(Component)]
struct HeavyCompute(bevy::tasks::Task<String>);

fn spawn_task(mut commands: Commands) {
    let pool = AsyncComputeTaskPool::get();
    let task = pool.spawn(async move {
        info!("后台任务开始:模拟 2 秒重计算");
        // 模拟耗时计算
        let mut sum: u64 = 0;
        for i in 0..100_000_000u64 {
            sum = sum.wrapping_add(i);
        }
        format!("计算完成,结果 = {sum}")
    });
    commands.spawn(HeavyCompute(task));
    info!("异步任务已提交,主线程不阻塞");
}

fn poll_task(mut commands: Commands, mut query: Query<(Entity, &mut HeavyCompute)>) {
    for (entity, mut task) in &mut query {
        if let Some(result) = bevy::tasks::futures::check_ready(&mut task.0) {
            info!("后台任务返回: {result}");
            commands.entity(entity).despawn();
        }
    }
}

Listing 34-2:AsyncComputeTaskPool——后台重计算不阻塞主线程

check_ready vs block_on

bevy::tasks::futures::check_ready 只做一次非阻塞轮询:future 就绪就返回 Some(output),否则返回 None。这比 block_on 安全得多——block_on 会阻塞主线程直到 future 完成,违背了异步的初衷。

注意:check_ready 要求 Task<T>: UnpinTask 实现了 Unpin,所以可以直接传入。

Task 的生命周期

  • Task<T> 被 drop 时,任务会被取消(cancel)
  • 想要"发后不管",调用 .detach()
  • .cancel() 是显式取消,会等待任务停止并返回 Option<T>
  • .is_finished() 检查任务是否已完成(不阻塞)

CommandQueue 模式

官方示例 async_compute.rs 展示了一种高级模式:后台任务返回 CommandQueue,里面装着一个 FnOnce(&mut World) 闭包。主线程用 commands.append(&mut queue) 把闭包应用到世界中。这样后台任务可以安全地构造 ECS 修改,由主线程统一执行。