ezEngine  Release 25.03
ezTaskSystem Class Reference

This system allows to automatically distribute tasks onto a number of worker threads. More...

#include <TaskSystem.h>

Classes

struct  TaskData
 

Static Public Member Functions

Utilities
static void WriteStateSnapshotToDGML (ezDGMLGraph &ref_graph)
 Writes the internal state of the ezTaskSystem as a DGML graph.
 
static void WriteStateSnapshotToFile (const char *szPath=nullptr)
 Convenience function to write the task graph snapshot to a file. If no path is given, the file is written to ":appdata/TaskGraphs/__date__.dgml".
 

Managing Tasks

static ezTaskGroupID StartSingleTask (const ezSharedPtr< ezTask > &pTask, ezTaskPriority::Enum priority, ezOnTaskGroupFinishedCallback callback=ezOnTaskGroupFinishedCallback())
 A helper function to insert a single task into the system and start it right away. Returns ID of the Group into which the task has been put.
 
static ezTaskGroupID StartSingleTask (const ezSharedPtr< ezTask > &pTask, ezTaskPriority::Enum priority, ezTaskGroupID dependency, ezOnTaskGroupFinishedCallback callback=ezOnTaskGroupFinishedCallback())
 A helper function to insert a single task into the system and start it right away. Returns ID of the Group into which the task has been put. This overload allows to additionally specify a single dependency.
 
static void FinishFrameTasks ()
 Call this function once at the end of a frame. It will ensure that all tasks for 'this frame' get finished properly. More...
 
static ezResult CancelTask (const ezSharedPtr< ezTask > &pTask, ezOnTaskRunning::Enum onTaskRunning=ezOnTaskRunning::WaitTillFinished)
 This function will try to remove the given task from the work queue, to prevent it from being executed. More...
 
static void BroadcastClearThreadLocalsEvent ()
 Broadcasts ezThreadEvent::ClearThreadLocals on all worker threads.
 

Managing Task Groups

static ezTaskGroupID CreateTaskGroup (ezTaskPriority::Enum priority, ezOnTaskGroupFinishedCallback callback=ezOnTaskGroupFinishedCallback())
 Creates a new task group for one-time use. Groups need to be recreated every time a task is supposed to be inserted into the system. More...
 
static void AddTaskToGroup (ezTaskGroupID group, const ezSharedPtr< ezTask > &pTask)
 Adds a task to the given task group. The group must not yet have been started.
 
static void AddTaskGroupDependency (ezTaskGroupID group, ezTaskGroupID dependsOn)
 Adds a dependency on another group to Group. This means Group will not be execute before DependsOn has finished. More...
 
static void AddTaskGroupDependencyBatch (ezArrayPtr< const ezTaskGroupDependency > batch)
 Same as AddTaskGroupDependency() but batches multiple dependency additions.
 
static void StartTaskGroup (ezTaskGroupID group)
 Starts the task group. After this no further modifications on the group (new tasks or dependencies) are allowed.
 
static void StartTaskGroupBatch (ezArrayPtr< const ezTaskGroupID > batch)
 Same as StartTaskGroup() but batches multiple actions.
 
static bool IsTaskGroupFinished (ezTaskGroupID group)
 Returns whether the given Group id refers to a task group that has been finished already. More...
 
static ezResult CancelGroup (ezTaskGroupID group, ezOnTaskRunning::Enum onTaskRunning=ezOnTaskRunning::WaitTillFinished)
 Cancels all the tasks in the given group. More...
 
static void WaitForGroup (ezTaskGroupID group)
 Blocks until all tasks in the given group have finished. More...
 
static void WaitForCondition (ezDelegate< bool()> condition)
 Blocks the current thread until the given delegate returns true. More...
 

Thread Management

class ezTaskWorkerThread
 
static void SetWorkerThreadCount (ezInt32 iShortTasks=-1, ezInt32 iLongTasks=-1)
 Sets the number of threads to use for the different task categories. More...
 
static ezUInt32 GetWorkerThreadCount (ezWorkerThreadType::Enum type)
 Returns the maximum number of threads that should work on the given type of task at the same time.
 
static ezUInt32 GetNumAllocatedWorkerThreads (ezWorkerThreadType::Enum type)
 Returns the number of threads that have been allocated to potentially work on the given type of task. More...
 
static ezWorkerThreadType::Enum GetCurrentThreadWorkerType ()
 Returns the (thread local) type of tasks that would be executed on this thread.
 
static double GetThreadUtilization (ezWorkerThreadType::Enum type, ezUInt32 uiThreadIndex, ezUInt32 *pNumTasksExecuted=nullptr)
 Returns the utilization (0.0 to 1.0) of the given thread. Note: This will only be valid, if FinishFrameTasks() is called once per frame. More...
 
static void WakeUpThreads (ezWorkerThreadType::Enum type, ezUInt32 uiNumThreads)
 [internal] Wakes up or allocates up to uiNumThreads, unless enough threads are currently active and not blocked
 

Parallel For

static void ParallelForIndexed (ezUInt32 uiStartIndex, ezUInt32 uiNumItems, ezParallelForIndexedFunction32 taskCallback, const char *szTaskName=nullptr, ezTaskNesting taskNesting=ezTaskNesting::Never, const ezParallelForParams &params=ezParallelForParams())
 A helper function to process task items in a parallel fashion by having per-worker index ranges generated.
 
static void ParallelForIndexed (ezUInt64 uiStartIndex, ezUInt64 uiNumItems, ezParallelForIndexedFunction64 taskCallback, const char *szTaskName=nullptr, ezTaskNesting taskNesting=ezTaskNesting::Never, const ezParallelForParams &params=ezParallelForParams())
 A helper function to process task items in a parallel fashion by having per-worker index ranges generated.
 
template<typename ElemType , typename Callback >
static void ParallelFor (ezArrayPtr< ElemType > taskItems, Callback taskCallback, const char *szTaskName=nullptr, const ezParallelForParams &params=ezParallelForParams())
 
template<typename ElemType , typename Callback >
static void ParallelForSingle (ezArrayPtr< ElemType > taskItems, Callback taskCallback, const char *szTaskName=nullptr, const ezParallelForParams &params=ezParallelForParams())
 
template<typename ElemType , typename Callback >
static void ParallelForSingleIndex (ezArrayPtr< ElemType > taskItems, Callback taskCallback, const char *szTaskName=nullptr, const ezParallelForParams &params=ezParallelForParams())
 

Misc

static void SetTargetFrameTime (ezTime targetFrameTime=ezTime::MakeFromSeconds(1.0/40.0))
 Sets the target frame time that is supposed to not be exceeded. More...
 

Detailed Description

This system allows to automatically distribute tasks onto a number of worker threads.

By deriving from ezTask you can create your own task types. These can be executed through this task system. You can run a single task using the 'StartSingleTask' function. For more complex setups, it is possible to create groups of tasks, which can have interdependencies. This should be used to group all tasks that belong to one system and need to be done before another system runs. For example you could group all tasks to update particle systems, and then have another group for all tasks to update sound, which depends on the first group, such that sound is only updated after all particle systems are done with.

Although it is possible to wait for tasks or to cancel them, it is generally advised to try to minimize their use. Tasks that might need to be canceled regularly (e.g. path searches) should be implemented in a way that they are aware of being canceled and will stop their work prematurely, instead of running through to the end.

Note that it is crucial to call 'FinishFrameTasks' once per frame, otherwise tasks that need to be executed on the main thread are never executed.

Member Function Documentation

◆ AddTaskGroupDependency()

void ezTaskSystem::AddTaskGroupDependency ( ezTaskGroupID  group,
ezTaskGroupID  dependsOn 
)
static

Adds a dependency on another group to Group. This means Group will not be execute before DependsOn has finished.

Note
Be careful with dependencies and task priorities. A task that has to execute 'this frame' should never depend on a task that needs only finish 'next frame', this might introduce very long and unnecessary waits. A task that has priority 'this frame' or 'next frame' will actually not be executed in 'this frame' or 'next frame' until all its dependencies are fulfilled. So you might add a long running task and a short task which depends on it, but the system will not block at the end of the frame, to wait for the long running task (to finish the short task thereafter), as that short task won't get scheduled for execution, at all, until all its dependencies are actually finished.

◆ CancelGroup()

ezResult ezTaskSystem::CancelGroup ( ezTaskGroupID  group,
ezOnTaskRunning::Enum  onTaskRunning = ezOnTaskRunning::WaitTillFinished 
)
static

Cancels all the tasks in the given group.

EZ_SUCCESS is returned, if all tasks were already finished or could be removed without waiting for any of them. EZ_FAILURE is returned, if at least one task was being processed by another thread and could not be removed without waiting. If bWaitForIt is false, the function cancels all tasks, but returns without blocking, even if not all tasks have been finished. If bWaitForIt is true, the function returns only after it is guaranteed that all tasks are properly terminated.

◆ CancelTask()

ezResult ezTaskSystem::CancelTask ( const ezSharedPtr< ezTask > &  pTask,
ezOnTaskRunning::Enum  onTaskRunning = ezOnTaskRunning::WaitTillFinished 
)
static

This function will try to remove the given task from the work queue, to prevent it from being executed.

The function will return EZ_SUCCESS, if the task could be removed and thus its execution could be prevented. It will also return EZ_SUCCESS, if the task was already finished and nothing needed to be done. Tasks that are removed without execution will still be marked as 'finished' and dependent tasks will be scheduled.

EZ_FAILURE is returned, if the task had already been started and thus could not be prevented from running.

In case of failure, bWaitForIt determines whether 'WaitForTask' is called (with all its consequences), or whether the function will return immediately.

The cancel flag is set on the task, such that tasks that support canceling might terminate earlier. However, there is no guarantee how long it takes for already running tasks to actually finish. Therefore when bWaitForIt is true, this function might block for a very long time. It is advised to implement tasks that need to be canceled regularly (e.g. path searches for units that might die) in a way that allows for quick canceling.

◆ CreateTaskGroup()

ezTaskGroupID ezTaskSystem::CreateTaskGroup ( ezTaskPriority::Enum  priority,
ezOnTaskGroupFinishedCallback  callback = ezOnTaskGroupFinishedCallback() 
)
static

Creates a new task group for one-time use. Groups need to be recreated every time a task is supposed to be inserted into the system.

All tasks that are added to this group will be run with the same given Priority. Once all tasks in the group are finished and thus the group is finished, an optional Callback can be executed.

◆ FinishFrameTasks()

void ezTaskSystem::FinishFrameTasks ( )
static

Call this function once at the end of a frame. It will ensure that all tasks for 'this frame' get finished properly.

Calling this function is crucial for several reasons. It is the central function to execute 'main thread' tasks. Otherwise these tasks might never get executed. It also changes the priority of all 'next frame' tasks to 'this frame', so that those tasks are guaranteed to get finished when 'FinishFrameTasks' is called the next time.

Finally this function executes tasks with the priority 'SomeFrameMainThread' as long as the target frame time is not exceeded. You can configure this with SetTargetFrameTime(), which defines how long (in milliseconds) the frame is allowed to be. As long as that time is not exceeded, additional 'SomeFrameMainThread' tasks will be executed. If the frame time spikes for a few frames, no such tasks will be executed, to prevent making it worse. However, if the frame time stays high over a longer period, 'FinishFrameTasks' will execute 'SomeFrameMainThread' tasks every once in a while, to guarantee some progress.

Note
After this function returns all tasks of priority 'ThisFrameMainThread' are finished. All tasks of priority 'EarlyThisFrame' up to 'LateThisFrame' are either finished or currently running on some thread, so they will be finished soon. There is however no guarantee that they are indeed all finished, as that would introduce unnecessary stalls.

◆ GetNumAllocatedWorkerThreads()

ezUInt32 ezTaskSystem::GetNumAllocatedWorkerThreads ( ezWorkerThreadType::Enum  type)
static

Returns the number of threads that have been allocated to potentially work on the given type of task.

CAREFUL! This is not the number of threads that will be active at the same time. Use GetWorkerThreadCount() for that. This is the maximum number of threads that may jump in, if too many threads are blocked. This number will change dynamically at runtime to prevent deadlocks and it can grow very, very large.

◆ GetThreadUtilization()

double ezTaskSystem::GetThreadUtilization ( ezWorkerThreadType::Enum  type,
ezUInt32  uiThreadIndex,
ezUInt32 *  pNumTasksExecuted = nullptr 
)
static

Returns the utilization (0.0 to 1.0) of the given thread. Note: This will only be valid, if FinishFrameTasks() is called once per frame.

Also optionally returns the number of tasks that were finished during the last frame.

◆ IsTaskGroupFinished()

bool ezTaskSystem::IsTaskGroupFinished ( ezTaskGroupID  group)
static

Returns whether the given Group id refers to a task group that has been finished already.

There is no time frame in which group IDs are valid. You may call this function at any time, even 10 minutes later, and it will correctly determine the results.

◆ ParallelFor()

template<typename ElemType , typename Callback >
void ezTaskSystem::ParallelFor ( ezArrayPtr< ElemType >  taskItems,
Callback  taskCallback,
const char *  szTaskName = nullptr,
const ezParallelForParams params = ezParallelForParams() 
)
static

A helper function to process task items in a parallel fashion by generating per-worker sub-ranges from an initial item array pointer. Given an array pointer 'taskItems' with elements of type ElemType, the following invocations are possible:

◆ ParallelForSingle()

template<typename ElemType , typename Callback >
void ezTaskSystem::ParallelForSingle ( ezArrayPtr< ElemType >  taskItems,
Callback  taskCallback,
const char *  szTaskName = nullptr,
const ezParallelForParams params = ezParallelForParams() 
)
static

A helper function to process task items in a parallel fashion and one-by-one (without global index). Given an array pointer 'taskItems' with elements of type ElemType, the following invocations are possible:

  • ParallelFor(taskItems, [](ElemType taskItem) { });
  • ParallelFor(taskItems, [](ElemType& taskItem) { });
  • ParallelFor(taskItems, [](const ElemType& taskItem) { });

◆ ParallelForSingleIndex()

template<typename ElemType , typename Callback >
void ezTaskSystem::ParallelForSingleIndex ( ezArrayPtr< ElemType >  taskItems,
Callback  taskCallback,
const char *  szTaskName = nullptr,
const ezParallelForParams params = ezParallelForParams() 
)
static

A helper function to process task items in a parallel fashion and one-by-one (with global index). Given an array pointer 'taskItems' with elements of type ElemType, the following invocations are possible:

  • ParallelFor(taskItems, [](ezUInt32 globalTaskItemIndex, ElemType taskItem) { });
  • ParallelFor(taskItems, [](ezUInt32 globalTaskItemIndex, ElemType& taskItem) { });
  • ParallelFor(taskItems, [](ezUInt32 globalTaskItemIndex, const ElemType& taskItem) { });

◆ SetTargetFrameTime()

void ezTaskSystem::SetTargetFrameTime ( ezTime  targetFrameTime = ezTime::MakeFromSeconds(1.0 / 40.0))
static

Sets the target frame time that is supposed to not be exceeded.

See also
FinishFrameTasks() for more details.

◆ SetWorkerThreadCount()

void ezTaskSystem::SetWorkerThreadCount ( ezInt32  iShortTasks = -1,
ezInt32  iLongTasks = -1 
)
static

Sets the number of threads to use for the different task categories.

uiShortTasks and uiLongTasks must be at least 1 and should not exceed the number of available CPU cores. There will always be exactly one additional thread for file access tasks (ezTaskPriority::FileAccess).

If uiShortTasks or uiLongTasks is smaller than 1, a default number of threads will be used for that type of work. This number of threads depends on the number of available CPU cores. If SetWorkThreadCount is never called, at all, the first time any task is started the number of worker threads is set to this default configuration. Unless you have a good idea how to set up the number of worker threads to make good use of the available cores, it is a good idea to just use the default settings.

◆ WaitForCondition()

void ezTaskSystem::WaitForCondition ( ezDelegate< bool()>  condition)
static

Blocks the current thread until the given delegate returns true.

If possible, prefer to use WaitForGroup() to wait for some task to finish, as that is the most efficient way. If not possible, prefer to use WaitForCondition() instead of rolling your own busy-loop for polling some state. WaitForCondition() will NOT put the current thread to sleep, but instead keep polling the delegate. However, in between, it will try to execute other tasks and if there are no tasks that it could take on, it will wake up another worker thread thus guaranteeing, that there are enough unblocked threads in the system to do all the work.

◆ WaitForGroup()

void ezTaskSystem::WaitForGroup ( ezTaskGroupID  group)
static

Blocks until all tasks in the given group have finished.

If you need to wait for some other task to finish, this should always be the preferred method to do so. WaitForGroup will put the current thread to sleep and use thread signals to only wake it up again once the group is indeed finished. This is the most efficient way to wait for a task.


The documentation for this class was generated from the following files: