异步计算: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>: Unpin。Task 实现了 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 修改,由主线程统一执行。