Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
WorkerThreadPool
继承: Object
单例,启动时会分配一些 Thread,可以将任务卸载到这些线程中执行。
描述
WorkerThreadPool 单例在项目启动时会分配一组 Thread(称作工作线程)并提供将任务卸载至这些线程上执行的方法。这样就能够简化多线程的使用,不必创建 Thread。
任务里放置的是要让线程执行的 Callable。WorkerThreadPool 既可以创建常规任务也可以创建分组任务,常规任务由单个工作线程执行,而分组任务可以分布在多个工作线程执行。分组任务会多次执行同一个 Callable,可用于遍历大量的元素,例如场景中的敌人。
以下是将开销很大的函数卸载到工作线程执行的例子:
var enemies = [] # 用敌人填充的数组。
func process_enemy_ai(enemy_index):
var processed_enemy = enemies[enemy_index]
# 开销很大的逻辑……
func _process(delta):
var task_id = WorkerThreadPool.add_group_task(process_enemy_ai, enemies.size())
# 其他代码……
WorkerThreadPool.wait_for_group_task_completion(task_id)
# 要求敌人 AI 已经处理完毕的其他代码。
private List<Node> _enemies = new List<Node>(); // 用敌人填充的数组。
private void ProcessEnemyAI(int enemyIndex)
{
Node processedEnemy = _enemies[enemyIndex];
// 开销很大的逻辑……
}
public override void _Process(double delta)
{
long taskId = WorkerThreadPool.AddGroupTask(Callable.From<int>(ProcessEnemyAI), _enemies.Count);
// 其他代码……
WorkerThreadPool.WaitForGroupTaskCompletion(taskId);
// 要求敌人 AI 已经处理完毕的其他代码。
}
以上代码要求 enemies
数组中的元素个数在多线程部分执行时保持不变。
注意:如果分布到多个线程执行的任务在计算方面的开销并不大,那么使用这个单例可能对性能有负面影响。
教程
方法
add_group_task(action: Callable, elements: int, tasks_needed: int = -1, high_priority: bool = false, description: String = "") |
|
add_task(action: Callable, high_priority: bool = false, description: String = "") |
|
get_group_processed_element_count(group_id: int) const |
|
is_group_task_completed(group_id: int) const |
|
is_task_completed(task_id: int) const |
|
void |
wait_for_group_task_completion(group_id: int) |
wait_for_task_completion(task_id: int) |
方法说明
int add_group_task(action: Callable, elements: int, tasks_needed: int = -1, high_priority: bool = false, description: String = "") 🔗
将 action
添加为分组任务,让多个工作线程执行。该 Callable 的调用次数由 elements
决定,第一个调用的线程使用 0
作为参数,后续执行时会将其加 1,直到变为 element - 1
。
任务分布的线程数由 tasks_needed
定义,默认值 -1
表示分布到所有工作线程。high_priority
决定的是任务具有高优先级还是低优先级(默认)。你还可以选择提供 description
作为描述信息,方便调试。
返回分组任务 ID,可用于其他方法。
警告:每个任务都必须在某处使用 wait_for_task_completion() 或 wait_for_group_task_completion() 等待完成,从而清理任务中分配的资源。
int add_task(action: Callable, high_priority: bool = false, description: String = "") 🔗
将 action
添加为分组任务,让单个工作线程执行。high_priority
决定的是任务具有高优先级还是低优先级(默认)。你还可以选择提供 description
作为描述信息,方便调试。
返回任务 ID,可用于其他方法。
警告:每个任务都必须在某处使用 wait_for_task_completion() 或 wait_for_group_task_completion() 等待完成,从而清理任务中分配的资源。
int get_group_processed_element_count(group_id: int) const 🔗
返回具有给定 ID 的分组任务的 Callable 已经被工作线程执行的次数。
注意:线程已经开始执行 Callable 但尚未完成的情况不计算在内。
bool is_group_task_completed(group_id: int) const 🔗
如果 ID 对应的分组任务已完成,则返回 true
。
注意:只应该在添加分组任务之后、等待完成之前调用该方法。
bool is_task_completed(task_id: int) const 🔗
如果 ID 对应的任务已完成,则返回 true
。
注意:只应该在添加分组任务之后、等待完成之前调用该方法。
void wait_for_group_task_completion(group_id: int) 🔗
在具有给定 ID 的分组任务完成前暂停调用这个方法的线程。
Error wait_for_task_completion(task_id: int) 🔗
暂停调用该方法的线程,直到给定 ID 对应的任务完成。
如果能够成功等待任务,则返回 @GlobalScope.OK。
如果不存在与传入 ID 对应的任务(可能已被等待或处理),则返回 @GlobalScope.ERR_INVALID_PARAMETER。
如果其他正在执行的任务调用了该方法,并且由于任务调度的原因,存在死锁的可能性(例如,要等待的任务可能位于调用堆栈中的较低级别,因此不能继续),则返回 @GlobalScope.ERR_BUSY。这是比较高级的情况,只有任务之间存在依赖关系(在当前实现中,棘手的情况是尝试等待较旧任务的任务)时才会出现。