虚幻4 Task小记

本文介绍了虚幻4中TGraphTask的FConstructor类如何根据EVENT数组判断任务创建条件。当所有依赖事件触发时,TGraphTask执行并通知相关FConstructor,允许符合条件的构造器创建新的任务,否则继续等待。这种机制确保了任务执行的正确顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TGraphTask类里面有一个FConstructor类,用来创建TGraphTask。

		FGraphEventRef ConstructAndDispatchWhenReady(T&&... Args)
		{
			new ((void *)&Owner->TaskStorage) TTask(Forward<T>(Args)...);
			return Owner->Setup(Prerequisites, CurrentThreadIfKnown);
		}

这个就直接字面意思,创建一个TGraphTask,但是等他的prerequisites都触发了,才把这个任务分发。也就是进入队列。


		FConstructor(TGraphTask* InOwner, const FGraphEventArray* InPrerequisites, ENamedThreads::Type InCurrentThreadIfKnown)
			: Owner(InOwner)
			, Prerequisites(InPrerequisites)
			, CurrentThreadIfKnown(InCurrentThreadIfKnown)
		{
		}



创建的时候可以传入一个EVENT数组,这个Constructor创建TGraphTask时候回判断需求的event是否都已经触发了,如果没有触发,那就先不创建。

	void SetupPrereqs(const FGraphEventArray* Prerequisites, ENamedThreads::Type CurrentThreadIfKnown, bool bUnlock)
	{
		checkThreadGraph(!TaskConstructed);
		TaskConstructed = true;
		TTask& Task = *(TTask*)&TaskStorage;
		SetThreadToExecuteOn(Task.GetDesiredThread());
		int32 AlreadyCompletedPrerequisites = 0;
		if (Prerequisites)
		{
			for (int32 Index = 0; Index < Prerequisites->Num(); Index++)
			{
				check((*Prerequisites)[Index]);
				if (!(*Prerequisites)[Index]->AddSubsequent(this))
				{
					AlreadyCompletedPrerequisites++;
				}
			}
		}
		PrerequisitesComplete(CurrentThreadIfKnown, AlreadyCompletedPrerequisites, bUnlock);
	}



PrerequisitesComplete

函数就是判断是否所有event都触发过的函数。


TGraphTask里面有一个变量

	/** A reference counted pointer to the completion event which lists the tasks that have me as a prerequisite. **/
	FGraphEventRef				Subsequents;



当这个任务执行完时候,会触发这个event,这个event保存了对于他有依赖的所有FConstructor,是一个数组,

然后会告诉constructor,这个event触发了,看看是不是可以执行了。

然后一堆constructor有的发现确实所需要的event都触发了,就创建一个task,有的发现还有没有触发的event,就不创建,继续等待。



这个就是event触发时候调用的函数。

	/**
	 *	"Complete" the event. This grabs the list of subsequents and atomically closes it. Then for each subsequent it reduces the number of prerequisites outstanding and if that drops to zero, the task is queued.
	 *	@param CurrentThreadIfKnown if the current thread is known, provide it here. Otherwise it will be determined via TLS if any task ends up being queued.
	**/
	CORE_API void DispatchSubsequents(TArray<FBaseGraphTask*>& NewTasks, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread);


void FGraphEvent::DispatchSubsequents(TArray<FBaseGraphTask*>& NewTasks, ENamedThreads::Type CurrentThreadIfKnown)
{
	if (EventsToWaitFor.Num())
	{
		// need to save this first and empty the actual tail of the task might be recycled faster than it is cleared.
		FGraphEventArray TempEventsToWaitFor;
		Exchange(EventsToWaitFor, TempEventsToWaitFor);
		// create the Gather...this uses a special version of private CreateTask that "assumes" the subsequent list (which other threads might still be adding too).
		DECLARE_CYCLE_STAT(TEXT("FNullGraphTask.DontCompleteUntil"),
			STAT_FNullGraphTask_DontCompleteUntil,
			STATGROUP_TaskGraphTasks);

		TGraphTask<FNullGraphTask>::CreateTask(FGraphEventRef(this), &TempEventsToWaitFor, CurrentThreadIfKnown).ConstructAndDispatchWhenReady(GET_STATID(STAT_FNullGraphTask_DontCompleteUntil), ENamedThreads::HiPri(ENamedThreads::AnyThread));
		return;
	}

#if USE_NEW_LOCK_FREE_LISTS
	bComplete = true;
	int32 SpinCount = 0;
	while (true)
	{
		FBaseGraphTask* NewTask = SubsequentList.Pop();
		if (!NewTask)
		{
			if (SubsequentList.CloseIfEmpty())
			{
				break;
			}
			LockFreeCriticalSpin(SpinCount);
			continue;
		}
		NewTask->ConditionalQueueTask(CurrentThreadIfKnown);
	}
#else
	SubsequentList.PopAllAndClose(NewTasks);
	for (int32 Index = NewTasks.Num() - 1; Index >= 0 ; Index--) // reverse the order since PopAll is implicitly backwards
	{
		FBaseGraphTask* NewTask = NewTasks[Index];
		checkThreadGraph(NewTask);
		NewTask->ConditionalQueueTask(CurrentThreadIfKnown);
	}
	NewTasks.Reset();
#endif
}

NewTask->ConditionalQueueTask(CurrentThreadIfKnown);

能看到最后这个函数,就是让Task判断是否要进入队列了。

<pre name="code" class="cpp">	/** 
	 *	An indication that a prerequisite has been completed. Reduces the number of prerequisites by one and if no prerequisites are outstanding, it queues the task for execution.
	 *	@param CurrentThread; provides the index of the thread we are running on. This is handy for submitting new taks. Can be ENamedThreads::AnyThread if the current thread is unknown.
	 **/
	void ConditionalQueueTask(ENamedThreads::Type CurrentThread)
	{
		if (NumberOfPrerequistitesOutstanding.Decrement()==0)
		{
			QueueTask(CurrentThread);
		}
	}



就是让TGraphTask自己看看是不是可以执行了。

PrerequisitesComplete
	/** An aligned bit of storage to hold the embedded task **/
	TAlignedBytes<sizeof(TTask),ALIGNOF(TTask)> TaskStorage;

CreateTask
其实就是创建了一个Task,但是没有和我们自己宏创建的那个类有关联,就是创建了一个空壳。
ConstructAndDispatchWhenReady
才是吧自己宏创建的那个类实例化,放在TGraphTask的一个变量里面。
当执行这个task时候,执行自己的那个类的DoTask()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值