线程池:ComputeTaskPool、AsyncComputeTaskPool、IoTaskPool
Bevy 维护三个全局线程池,各有分工:
| 池 | 用途 | 特点 |
|---|---|---|
ComputeTaskPool | CPU 密集型计算 | 任务应在当帧完成 |
AsyncComputeTaskPool | 可跨帧的重计算 | 任务可以跑多帧 |
IoTaskPool | I/O 密集型操作 | 任务大部分时间在等待 |
三个池都是 TaskPool 的 newtype,通过 ::get() 获取全局实例。它们在 DefaultPlugins 初始化时自动创建。
TaskPool::scope:并行分治
scope 把一组任务分发到线程池,等待全部完成后收集结果:
rust
use bevy::prelude::*;
use bevy::tasks::ComputeTaskPool;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, parallel_work)
.run();
}
fn setup() {
info!("TaskPool 基础用法:并行计算素数");
}
fn parallel_work(mut ran: Local<bool>) {
if *ran {
return;
}
*ran = true;
let pool = ComputeTaskPool::get();
let numbers: Vec<u64> = (2..100_000).collect();
let results = pool.scope(|scope| {
for chunk in numbers.chunks(1000) {
let chunk = chunk.to_vec();
scope.spawn(async move {
chunk
.into_iter()
.filter(|&n| is_prime(n))
.collect::<Vec<_>>()
});
}
});
let total: usize = results.iter().map(|v| v.len()).sum();
info!("2..100000 中共找到 {total} 个素数");
}
fn is_prime(n: u64) -> bool {
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n % 2 == 0 || n % 3 == 0 {
return false;
}
let mut i = 5;
while i * i <= n {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
i += 6;
}
true
}Listing 34-1:TaskPool::scope——并行计算素数
scope 接受一个闭包,闭包接收 &Scope,通过 scope.spawn(async { ... }) 提交任务。所有任务完成后,scope 返回 Vec<T>——每个任务的输出。
关键约束:T: Send + 'static,任务的返回值必须可以在线程间传递。
TaskPool::spawn:单任务提交
如果不需要收集结果,直接用 spawn 提交一个 future:
rust
let pool = ComputeTaskPool::get();
let task = pool.spawn(async { 42 });
// task 是 Task<i32>,可以用 check_ready 轮询,或 .detach() 放弃结果Task<T> 实现了 Future,可以 .await。如果不需要结果,调用 .detach() 让任务在后台完成。
三个池的选择
- 需要当帧拿到结果 →
ComputeTaskPool - 计算量大、可以多帧完成 →
AsyncComputeTaskPool - 读文件、发网络请求 →
IoTaskPool
选错池不会导致错误,但会影响调度优先级。ComputeTaskPool 的任务优先级最高,因为它假设你需要结果来推进当前帧。