Task使用备忘

UE4 Task的使用,用于备忘,快速复制
本文包括使用Task Graph、Async Task、ParallelFor
包含大量代码的Low B文章

网上讲解UE4多线程的文章有很多可以参考,配合打断点观察。这里有几个简单的使用案例:

1. Task Graph

1.1 TaskGraph_Zero.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"

/*
	测试TaskGraph
	真正用于计算的Task
*/
class NORACOMPUTETOOLS_API FTaskGraph_Zero
{
public:
	// 构造时,传入数据
	FTaskGraph_Zero(int32 InTargetIndex, TArray<float>& InDataArray);

	// 固定写法
	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FTaskGraph_Zero, STATGROUP_TaskGraphTasks);
	}

	// 指定运行的线程
	static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyThread; }

	// 后续执行模式(如果其它task依赖于此task执行的结果时,需要使用TrackSubsequents)
	static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }

	// 执行内容
	// 这里传入了线程类型和完成时触发的事件
	void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& CompletionGraphEvent)
	{
		int32 MaxNum = TargetIndex * 100;
		for (int32 i = 0; i < MaxNum; ++i)
		{
			DataArray[TargetIndex] += i;
		}

		/*
			可以在DoTask中添加下面的语句确保在子Task执行完之前,CompletionGraphEvent不会触发,以保证此Task完成时,子Task也完成了

			CompletionGraphEvent->DontCompleteUntil(TGraphTask<FTaskGraphClass>*->GetCompletionEvent());

			还可以在此语句之后检查子Task是否已经执行完成,但不建议这么做,如果有需要可以建立依赖关系
		*/

		UE_LOG(LogTemp, Log, TEXT("%d"), TargetIndex);
	}

private:
	// 模拟数据
	int32 TargetIndex;
	TArray<float>& DataArray;
};


// 一个用来测试打印的委托
DECLARE_DELEGATE_OneParam(FGraphTaskDelegate, const FString&);
/*
	计算完之后,用于打印的Task;也可以在这里进行下一步工作,比如再发布一批作业
*/
class NORACOMPUTETOOLS_API FTaskGraph_Zero_Print
{
public:
	// 构造时,传入数据
	FTaskGraph_Zero_Print(TArray<float>& InDataArray, FGraphTaskDelegate InGraphTaskDelegate, volatile bool& bInDone);

	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FTaskGraph_Zero_Print, STATGROUP_TaskGraphTasks);
	}

	static ENamedThreads::Type GetDesiredThread() { return ENamedThreads::AnyThread; }
	static ESubsequentsMode::Type GetSubsequentsMode() { return ESubsequentsMode::TrackSubsequents; }

	void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& CompletionGraphEvent)
	{
		for (auto& Value : DataArray)
		{
			FString message = FString::Printf(TEXT("%f"), Value);
			GraphTaskDelegate.ExecuteIfBound(message);
		}
		bDone = true;
	}

private:
	TArray<float>& DataArray;
	FGraphTaskDelegate GraphTaskDelegate;
	volatile bool& bDone;
};

1.2 TaskGraph_Zero.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "TaskGraphs/TaskGraph_Zero.h"

FTaskGraph_Zero::FTaskGraph_Zero(int32 InTargetIndex, TArray<float>& InDataArray)
	: TargetIndex(InTargetIndex), DataArray(InDataArray)
{
	
}

FTaskGraph_Zero_Print::FTaskGraph_Zero_Print(TArray<float>& InDataArray, FGraphTaskDelegate InGraphTaskDelegate, volatile bool& bInDone)
	: DataArray(InDataArray), GraphTaskDelegate(InGraphTaskDelegate), bDone(bInDone)
{
	
}

1.3 测试函数

void UNoraBPLibrary::NoraPrintf(const FString& InStr)
{
	UE_LOG(NoraBPLibraryLog, Log, TEXT("%s"),*InStr);
}
void UNoraBPLibrary::Nora_TG_Zero(TArray<float>& InTestArray, int32 TestArraySize)
{
	// 初始化数组
	InTestArray.Init(0.0, TestArraySize);

	// 创建Task数组 和 依赖事件数组
	TArray<TGraphTask<FTaskGraph_Zero>*> TaskArray;
	FGraphEventArray PrerequisiteEvents;

	// 填充
	for (int32 i = 0; i < TestArraySize; ++i)
	{
		auto* Task = TGraphTask<FTaskGraph_Zero>::CreateTask().ConstructAndHold(i, InTestArray);
		TaskArray.Add(Task);
		PrerequisiteEvents.Add(Task->GetCompletionEvent());
	}

	volatile bool bDone = false;

	// 创建打印 Task,依赖于计算Task
	FGraphTaskDelegate GraphTaskDelegate = FGraphTaskDelegate::CreateStatic(&UNoraBPLibrary::NoraPrintf);
	auto* PrintTask = TGraphTask<FTaskGraph_Zero_Print>::CreateTask(&PrerequisiteEvents).ConstructAndHold(InTestArray, GraphTaskDelegate, bDone);
	FGraphEventRef Event = PrintTask->GetCompletionEvent();

	// 触发
	PrintTask->Unlock();
	for (auto*& Task : TaskArray)
	{
		Task->Unlock();
	}

	while (!bDone)
	{
		//NoraPrintf(TEXT("Waiting...."));
	}
	
	NoraPrintf(TEXT("Done"));
}

2. Async Task

2.1 AsyncTask_Zero.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Async/AsyncWork.h"

class NORACOMPUTETOOLS_API FAsyncTask_Zero : public FNonAbandonableTask
{
	friend class FAsyncTask<FAsyncTask_Zero>;

	FORCEINLINE TStatId GetStatId() const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FAsyncTask_Zero, STATGROUP_ThreadPoolAsyncTasks);
	}

public:
	void DoWork();

	FAsyncTask_Zero(int32 InTargetIndex, TArray<float>& InDataArray);

private:
	int32 TargetIndex;
	TArray<float>& DataArray;
};

2.2 AsyncTask_Zero.cpp

// Fill out your copyright notice in the Description page of Project Settings.
#include "AsyncTasks/AsyncTask_Zero.h"

void FAsyncTask_Zero::DoWork()
{
	int32 MaxNum = TargetIndex * 10000;
	for (int32 i = 0; i < MaxNum; ++i)
	{
		DataArray[TargetIndex] += i;
	}

	UE_LOG(LogTemp, Log, TEXT("%d"), TargetIndex);
}

FAsyncTask_Zero::FAsyncTask_Zero(int32 InTargetIndex, TArray<float>& InDataArray)
	: TargetIndex(InTargetIndex), DataArray(InDataArray)
{
}

2.3 测试函数

// 初始化数组
InTestArray.Init(0.0, TestArraySize);

TArray<FAsyncTask<FAsyncTask_Zero>*> TaskArray;
for (int32 i = 0; i < TestArraySize; ++i)
{
	TaskArray.Add(new FAsyncTask<FAsyncTask_Zero>(i, InTestArray));
}
for (auto* Task : TaskArray)
{
	// 可以选择线程池
	Task->StartBackgroundTask();
}

for (auto* Task : TaskArray)
{
	Task->EnsureCompletion();
}

for (int32 i = 0; i < TestArraySize; ++i)
{
	UE_LOG(NoraBPLibraryLog, Log, TEXT("%f"), InTestArray[i]);
}

3. ParallelFor

#include "Async/ParallelFor.h"
//...........
ParallelFor(TestArraySize,
	[TestArraySize](int32 CurrIdx)
	{
		int32 Sum = 0;
		for (int32 Idx = 0; Idx < (TestArraySize - CurrIdx) * 1000; ++Idx)
		{
			Sum += FMath::Sqrt(34.5678910f);
		}

		UE_LOG(NoraBPLibraryLog, Log, TEXT("%d"), Sum);
	});


TArray<int32> TestArray;
ParallelFor(TestArraySize,
	[&](int32 Idx)
	{
		TestArray.Add(Idx);
	});

UE_LOG(NoraBPLibraryLog, Log, TEXT("----------%d"), TestArray.Num());
posted @ 2021-09-07 20:10  Miria  阅读(164)  评论(0)    收藏  举报