本文章只是我个人在学习虚幻引擎过程中的一些理解,不一定正确,若有说的不对的地方,欢迎指正。
前两篇我们分别讲了虚幻多线程的基础线程系统(FRunnable)和异步任务系统(AsyncTask),本篇我们来讲讲虚幻多线程的最后一员大将——TaskGraph。
TaskGraph字面意思是任务图,可以把它看成是超进化版的线程池,相对的它的源码也变得更加复杂。 TaskGraph实现了任务之间额等待机制,因此它和前两种多线程系统最大的区别在于它可以表示出任务与任务之间相互依赖的关系。
例如此刻我们有五个任务(A~E),各个任务彼此之间存在相互依赖的关系表示如下:
被依赖的任务指向依赖的任务(例如B和C依赖于A,即A必须比B、C先完成)
TaskGraph就可以通过自身机制来实现这种关系。
TaskGraph有三个主要组成部分:任务图抽象类(FTaskGraphImplementation、FTaskGraphInterface)、任务图中的线程抽象类(FWorkerThread)和任务抽象类(TGraphTask、FBaseGraphTask……)。
一.任务图抽象类:
1.FTaskGraphInterface
FTaskGraphInterface是TaskGraph系统主体的基类或者说是核心层,提供了一些系统需要的接口,但是还没有实现。源码在TaskGraphInterfaces.h文件中,具体路径在下方,FTaskGraphInterface大致源码如下:
class FTaskGraphInterface
{
friend class FBaseGraphTask;
virtual void QueueTask(class FBaseGraphTask* Task, ENamedThreads::Type ThreadToExecuteOn, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread) = 0;
public:
//……
static CORE_API void Startup(int32 NumThreads);
static CORE_API void Shutdown();
static CORE_API bool IsRunning();
static CORE_API FTaskGraphInterface& Get();
virtual ENamedThreads::Type GetCurrentThreadIfKnown(bool bLocalQueue = false) = 0;
virtual int32 GetNumWorkerThreads() = 0;
//……
virtual void AttachToThread(ENamedThreads::Type CurrentThread) = 0;
//……
virtual void WaitUntilTasksComplete(const FGraphEventArray& Tasks, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread) = 0;
virtual void TriggerEventWhenTasksComplete(FEvent* InEvent, const FGraphEventArray& Tasks, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread, ENamedThreads::Type TriggerThread = ENamedThreads::AnyHiPriThreadHiPriTask) = 0;
void WaitUntilTaskCompletes(const FGraphEventRef& Task, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread)
{
//……
}
void TriggerEventWhenTaskCompletes(FEvent* InEvent, const FGraphEventRef& Task, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread, ENamedThreads::Type TriggerThread = ENamedThreads::AnyHiPriThreadHiPriTask)
{
//……
}
//……
};
FTaskGraphInterface提供接口如下:
QueueTask——根据线程把任务放入到对应的队列中,函数接收三个参数,任务实例(Task)、执行任务的线程(ThreadToExecuteOn)、当前线程(CurrentThreadIfKnown);
Startup——函数初始化TaskGraph系统;
Shutdown——当TaskGraph系统处于空闲状态时,函数关闭系统,否则不起作用;
IsRunning——判断TaskGraph系统是否在运行;
Get——获取TaskGraph系统单例;
GetCurrentThreadIfKnown——获取当前线程;
GetNumWorkerThreads——获取工作线程数;
AttachToThread——把指定线程加入TaskGraph;
WaitUntilTasksComplete——执行多个异步任务,然后等待这些任务完成,接收两个参数,异步任务列表(Tasks)、当前线程(CurrentThreadIfKnown),必须是一个命名线程;
TriggerEventWhenTasksComplete——执行多个异步任务,完成时触发一个事件,接收三个参数,触发的事件(InEvent)、异步任务列表(Tasks)、当前线程(CurrentThreadIfKnown);
WaitUntilTaskCompletes——执行单个异步任务,然后等待该任务完成,接收两个参数,异步任务(Task)、当前线程(CurrentThreadIfKnown),必须是一个命名线程;
TriggerEventWhenTaskCompletes——执行单个异步任务,完成时触发一个事件,接收三个参数,触发的事件(InEvent)、异步任务(Task)、当前线程(CurrentThreadIfKnown)。
注:Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h
2.FTaskGraphImplementation:
FTaskGraphImplementation是FTaskGraphInterface的功能实现类/表现层,它实现了核心层提供的接口和一些必要的属性,我们只列举几个FTaskGraphInterface没有的接口和属性,大致源码如下:
class FTaskGraphImplementation : public FTaskGraphInterface
{
public:
//……
void StartTaskThread(int32 Priority, int32 IndexToStart)
{
//……
}
void StartAllTaskThreads(bool bDoBackgroundThreads)
{
//……
}
FBaseGraphTask* FindWork(ENamedThreads::Type ThreadInNeed)
{
//……
}
//……
void SetTaskThreadPriorities(EThreadPriority Pri)
{
//……
}
private:
//……
FWorkerThread WorkerThreads[MAX_THREADS];
int32 NumThreads;
int32 NumNamedThreads;
int32 NumTaskThreadSets;
int32 NumTaskThreadsPerSet;
bool bCreatedHiPriorityThreads;
bool bCreatedBackgroundPriorityThreads;
//……
};
接口介绍:
StartTaskThread——根据传入的优先级(Priority)和线程索引(IndexToStart)启动一个线程用来执行任务;
StartAllTaskThreads——启动所有线程执行任务;
FindWork——获取一个任务;
SetTaskThreadPriorities——顾名思义,设置线程的优先级;
属性介绍:
WorkerThreads[MAX_THREADS]——类型为FWorkerThread,是工作线程和数据的封装;
NumThreads——类型为32位整数,表示真正使用的线程数;
NumNamedThreads——类型为32位整数,表示NameThread线程的数量;
NumTaskThreadSets——类型为32位整数,表示线程集的数量;
NumTaskThreadsPerSet——类型为32位整数,表示每个线程集的线程数量,至少1个,最多3个;
bCreatedHiPriorityThreads——类型为布尔值,是一个标志,表示是否创建高先级线程;
bCreatedBackgroundPriorityThreads——类型为布尔值,是一个标志,表示是否创建低优先级线程;