UE多线程分为3种。
1.AsyneTask
2.TaskGrop
3. FRunnable
.前两种应该是属于异步
1.
.h
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Async/AsyncWork.h"
#include "MyAsyncAction.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMyAsyncTaskNodeResult, int, Result);
/**
*
*/
UCLASS()
class EDITORTOOLSDEMO_API UMyAsyncAction : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite)
bool bSelfRun = false;
UPROPERTY(BlueprintAssignable)
FMyAsyncTaskNodeResult OnSuccess;
UPROPERTY(BlueprintAssignable)
FMyAsyncTaskNodeResult OnFail;
/// <summary>
/// 自动销毁的FAutoDeleteAsyncTask
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
static UMyAsyncAction* AsyncTaskLoad(int MaxNumber);
/// <summary>
/// 手动销毁的FAsyncTask,目前没有实现手动删除。还是建议使用自动删除的AsyncTaskLoad
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
static UMyAsyncAction* SimpleAsyncTaskLoad(int MaxNumber);
void OnTaskFinish();
};
/// <summary>
/// FAutoDeleteAsyncTask:这同样是一个类模板,特点是Task执行完成之后无需手动垃圾回收 https://www.bilibili.com/read/cv9488877/
/// </summary>
class MyAsyncTask : FNonAbandonableTask
{
/// <summary>
/// 1.必备
/// </summary>
friend class FAutoDeleteAsyncTask<MyAsyncTask>;
/// <summary>
/// 自定义传值格式
/// </summary>
UMyAsyncAction* Node;
//当前计数
int32 CurrentCount = 0;
//总计数
int32 TotalCount;
MyAsyncTask(UMyAsyncAction* waitNode, int Target)
:Node(waitNode), TotalCount(Target) {}
~MyAsyncTask()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ClearAsyncTask"));
}
/// <summary>
/// 2.必备。 多线程工作函数
/// </summary>
void DoWork();
/// <summary>
/// 3.必备。根据不同名字修改"MyAsyncTask"
/// </summary>
/// <returns></returns>
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(MyAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
/// <summary>
/// FAsyncTask:这是一个类模板,特点是Task初始化之后会被加入到线程池中执行,但是执行结束之后需要手动进行垃圾回收 https://www.bilibili.com/read/cv9488877/
/// </summary>
class SimpleAsyncTask :FNonAbandonableTask
{
//如 MyAsyncTask 同注释
friend class FAsyncTask<SimpleAsyncTask>;
UMyAsyncAction* Node;
//当前计数
int32 CurrentCount = 0;
//总计数
int32 TotalCount;
public:
// 构造函数
SimpleAsyncTask(UMyAsyncAction* waitNode, int Target)
: Node(waitNode), TotalCount(Target) {}
// 析构函数
~SimpleAsyncTask()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, TEXT("ClearSimpleAsyncTask"));
}
// 具体任务逻辑执行
void DoWork();
// 固定写法,本类将作为函数参数
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(SimpleAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
.cpp
#include "AsyncLoad/MyAsyncAction.h"
UMyAsyncAction* UMyAsyncAction::AsyncTaskLoad(int MaxNumber)
{
UMyAsyncAction* Node = NewObject<UMyAsyncAction>();
///FAutoDeleteAsyncTask:这同样是一个类模板,特点是Task执行完成之后无需手动垃圾回收
auto MyTDeleteTask = new FAutoDeleteAsyncTask<MyAsyncTask>(Node, MaxNumber);
MyTDeleteTask->StartBackgroundTask();
return Node;
}
FAsyncTask<SimpleAsyncTask>* MyTask;
UMyAsyncAction* UMyAsyncAction::SimpleAsyncTaskLoad(int MaxNumber)
{
UMyAsyncAction* Node = NewObject<UMyAsyncAction>();
/// <summary>
/// FAsyncTask:这是一个类模板,特点是Task初始化之后会被加入到线程池中执行,但是执行结束之后需要手动进行垃圾回收
/// </summary>
MyTask = new FAsyncTask<SimpleAsyncTask>(Node, MaxNumber);
MyTask->StartBackgroundTask();
MyTask->StartSynchronousTask; 在当前线程执行,可能会导致主线程阻塞S
//GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("END111"));
/* MyTask->IsDone() 可以配合定时器检测是否完成任务
等待任务完成后,进行手动删除 [下面这段会导致卡顿。 目前没有实现手动删除。还是建议使用自动删除]
/*MyTask->EnsureCompletion();
delete MyTask;
MyTask = nullptr;*/
return Node;
}
//注意,需要在等待FAsyncTask结束后手动回收,如果FAsyncTask需要一段时间才能结束,可能需要在“Timer”中循环执行下面函数
void UMyAsyncAction::OnTaskFinish()
{
//使用IsDone()判断task是否完成
bool bTaskFinished = MyTask->IsDone();
if (bTaskFinished)
{
//手动回收内存
delete MyTask;
MyTask = nullptr;
}
}
void MyAsyncTask::DoWork()
{
while (CurrentCount < TotalCount)
{
/// <summary>
/// 判断是否在游戏进程中
/// </summary>
AsyncTask(ENamedThreads::GameThread, [=]()
{
//Node->OnSuccess.Broadcast(Actor->Counter);
CurrentCount++;
Node->OnSuccess.Broadcast(CurrentCount);
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, FString::Printf(TEXT("this is %d"), CurrentCount), true);
}
);
FPlatformProcess::Sleep(0.1f);
}
AsyncTask(ENamedThreads::GameThread, [=]()
{
Node->OnFail.Broadcast(-1);
}
);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ENDAsyncTask"));
/// <summary>
/// 跳出就是结束多线程
/// </summary>
return;
}
void SimpleAsyncTask::DoWork()
{
while (CurrentCount < TotalCount)
{
/// <summary>
/// 判断是否在游戏进程中
/// </summary>
AsyncTask(ENamedThreads::GameThread, [=]()
{
//Node->OnSuccess.Broadcast(Actor->Counter);
CurrentCount++;
Node->OnSuccess.Broadcast(CurrentCount);
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, FString::Printf(TEXT("this is %d"), CurrentCount), true);
Node->OnTaskFinish();
}
);
FPlatformProcess::Sleep(0.1f);
}
AsyncTask(ENamedThreads::GameThread, [=]()
{
Node->OnFail.Broadcast(-1);
}
);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("ENDSimpleAsyncTask"));
/// <summary>
/// 跳出就是结束多线程
/// </summary>
return;
}
2.
.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/AssetManager.h"
#include "MyAssetManager.generated.h"
DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadFinished);
/**
*
*/
UCLASS()
class EDITORTOOLSDEMO_API UMyAssetManager : public UAssetManager
{
GENERATED_BODY()
UPROPERTY()
FString LoadPackagePath;
bool bIsLoaded;
UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
void MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadFinished& OnAsyncLoadFinished);
UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
float GetLoadProgress();
};
.cpp
#include "AsyncLoad/MyAssetManager.h"
void UMyAssetManager::MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadFinished& OnAsyncLoadFinished)
{
//LoadPackagePath = FPaths::GetBaseFilename(, LoadPackageSoftObjectPath.ToString(), false);
LoadPackagePath = path.ToString();
bIsLoaded = false;
UE_LOG(LogTemp, Warning, TEXT("String: %s"), *LoadPackagePath);
LoadPackageAsync(
LoadPackagePath,
FLoadPackageAsyncDelegate::CreateLambda([=](const FName& PackageName, UPackage* LoadedPackage, EAsyncLoadingResult::Type Result)
{
if (Result == EAsyncLoadingResult::Failed)
{
UE_LOG(LogTemp, Warning, TEXT("Load Failed"));
}
else if (Result == EAsyncLoadingResult::Succeeded)
{
bIsLoaded = true;
UE_LOG(LogTemp, Warning, TEXT("Load Succeeded"));
OnAsyncLoadFinished.ExecuteIfBound();
}
}), 0, PKG_ContainsMap);
}
float UMyAssetManager::GetLoadProgress()
{
float FloatPercentage = GetAsyncLoadPercentage(*LoadPackagePath);
if (!bIsLoaded)
{
FString ResultStr = FString::Printf(TEXT("Percentage: %f"), FloatPercentage);
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, ResultStr);
UE_LOG(LogTemp, Warning, TEXT("Percentage: %f"), FloatPercentage);
}
else {
FloatPercentage = 100;
}
return FloatPercentage;
}
3.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Runtime/Core/Public/Async/TaskGraphInterfaces.h"
#include "AsyncLoadActor.generated.h"
/// <summary>
/// TaskGraph 系统是UE4一套抽象的异步任务处理系统可以创建任意多线程任务, 异步任务, 序列任务, 并行任务等,并可以指定任务顺序, 设置任务间的依赖, 最终形成一个任务图 https://zhuanlan.zhihu.com/p/430187330
/// 特点是操作简单,可以用来进行一些简单的运算,但是复杂运算请使用FRunnable. https://www.bilibili.com/read/cv9486649?spm_id_from=333.999.0.0
/// </summary>
class FTaskGraphTest
{
public:
FTaskGraphTest(int32 TotalCount) :TotalCount(TotalCount) {}
~FTaskGraphTest() {}
//最大计数
int32 TotalCount;
//初始计数
int32 CurrentCount = 0;
// 固定写法
FORCEINLINE TStatId GetStatId() const
{
//第一个参数传入自己创建的TaskGraph类型,第二个参数照写即可
RETURN_QUICK_DECLARE_CYCLE_STAT(FTaskGraph_SimpleTask, STATGROUP_TaskGraphTasks);
}
// 指定在哪个线程运行 ,该函数返回一个目标线程类型,决定taskgraph运行之后在哪个线程执行.
static ENamedThreads::Type GetDesiredThread()
{
/*
*在这里我们返回AnyNormalThreadNormalTask(优先级正常的线程)
*额外说明:
*1.可选线程有GameThread(主线程),AnyThread(任意线程),AudioThread(音频线程)等等
*2.线程逻辑在GameThread执行,可能会阻塞主线程,不可取
*3.线程逻辑很明显也不适合在音频线程,渲染线程之类的专职线程上进行
*4.AnyThread,字如其名,如果把线程逻辑放到AnyThread线程上执行,那这段线程逻辑有可能会
*被随机分配一个线程上执行,运气不好的话,分配渲染,物理,音频线程上执行就会出问题
*5.所以我们选择了对AnyThread进行限制,即:
*AnyNormalThreadNormalTask = AnyThread | NormalThreadPriority | NormalTaskPriority
*具有正常线程优先级和正常任务优先级的线程,这样就会避免线程逻辑运行在错误的线程上
*/
return ENamedThreads::AnyNormalThreadNormalTask;
}
// 后续执行模式 固定写法
static ESubsequentsMode::Type GetSubsequentsMode()
{
return ESubsequentsMode::TrackSubsequents;
}
// 线程逻辑执行函数
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
//本例在task中打印从0-TotalCount的数值,每次打印间隔1s
while (CurrentCount < TotalCount)
{
UE_LOG(LogTemp, Warning, TEXT("Current Count :%d"), CurrentCount);
CurrentCount++;
FPlatformProcess::Sleep(1.f);
}
}
};
/**
*FRunnable多线程 https://zhuanlan.zhihu.com/p/430187330 & https://blog.csdn.net/u011718663/article/details/118105259#_14
*/
class EDITORTOOLSDEMO_API FSimpleRunnable : public FRunnable
{
public:
FSimpleRunnable(const FString& ThreadName);
~FSimpleRunnable();
void PauseThread(); // 线程挂起 方法一
void WakeUpThread(); // 线程唤醒 方法一
void Suspend(bool bSuspend); // 线程挂起/唤醒 方法二
void StopThread(); // 停止线程,一般用该方法
void ShutDown(bool bShouldWait);// 停止线程,bShouldWait true的时候可强制 kill 线程
private:
FString m_ThreadName;
int32 m_ThreadID;
bool bRun = true; // 线程循环标志
bool bPause = false; //线程挂起标志
FRunnableThread* ThreadIns; // 线程实例
FEvent* ThreadEvent; //FEvent指针,挂起/激活线程, 在各自的线程内使用
/// <summary>
/// FRunnable 4个固有函数
/// </summary>
virtual bool Init() override;
/// <summary>
/// 执行函数
/// </summary>
virtual uint32 Run() override;
virtual void Stop() override;
virtual void Exit() override;
};
DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadLevel);
DECLARE_DYNAMIC_DELEGATE(FOnAsyncLoadFailed);
UCLASS()
class EDITORTOOLSDEMO_API AAsyncLoadActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AAsyncLoadActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
/// <summary>
/// TaskGraph生成
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void CreateTaskGraphSimpleTask(int32 TotalCount);
/// <summary>
/// 异步加载关卡 LoadingScreen https://blog.csdn.net/ttod/article/details/127716475 &https://www.bilibili.com/video/BV1Mr4y1A7nZ/?spm_id_from=333.337.search-card.all.click&vd_source=8a96d7f1981e32d11ac0eb4847916c44
/// </summary>
bool bIsLoaded;
/// <summary>
/// 加载包地址
/// </summary>
UPROPERTY(BlueprintReadWrite)
FString LoadPackagePath;
//UPROPERTY(EditAnywhere, BlueprintReadWrite)
// FSoftObjectPath LoadPackageSoftObjectPath;
/// <summary>
/// 生成异步加载关卡绑定回调
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
void MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadLevel& OnAsyncLoadFinished, const FOnAsyncLoadFailed& OnAsyncLoadFailed);
/// <summary>
/// 异步加载关卡进度
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadLevelTest")
float GetLoadProgress();
/// <summary>
/// FRunnable
/// </summary>
protected:
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
private:
static FSimpleRunnable* MySimpleRunnable; // 声明静态单例
public:
/// <summary>
/// FRunnable线程生成
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void CreateNewThread(const FString& ThreadName);
/// <summary>
/// FRunnable线程挂起
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void PauseThread();
/// <summary>
/// FRunnable程挂起/唤醒 方法二
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void SuspendThread(bool bSuspend);
/// <summary>
/// FRunnable线程唤醒
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void WakeUpThread();
/// <summary>
/// FRunnable停止
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void StopThread();
/// <summary>
/// FRunnable强制停止(有概率引起崩溃。建议:使用另一个停止)
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
void ForceKillThread(bool bShouldWait);
};
FSimpleRunnable* AAsyncLoadActor::MySimpleRunnable = nullptr;
.cpp
#include "AsyncLoad/AsyncLoadActor.h"
// Sets default values
AAsyncLoadActor::AAsyncLoadActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AAsyncLoadActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AAsyncLoadActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AAsyncLoadActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (MySimpleRunnable) // 防止线程挂起,退出无响应
{
MySimpleRunnable->StopThread();
delete MySimpleRunnable;
MySimpleRunnable = nullptr;
}
}
void AAsyncLoadActor::CreateNewThread(const FString& ThreadName)
{
if (MySimpleRunnable == nullptr)
{
MySimpleRunnable = new FSimpleRunnable(ThreadName);
}
}
void AAsyncLoadActor::PauseThread()
{
if (MySimpleRunnable)
{
MySimpleRunnable->PauseThread();
}
}
void AAsyncLoadActor::SuspendThread(bool bSuspend)
{
if (MySimpleRunnable)
{
MySimpleRunnable->Suspend(bSuspend);
}
}
void AAsyncLoadActor::WakeUpThread()
{
if (MySimpleRunnable)
{
MySimpleRunnable->WakeUpThread();
}
}
void AAsyncLoadActor::StopThread()
{
if (MySimpleRunnable)
{
MySimpleRunnable->StopThread();
}
}
void AAsyncLoadActor::ForceKillThread(bool bShouldWait)
{
if (MySimpleRunnable)
{
MySimpleRunnable->ShutDown(bShouldWait);
delete MySimpleRunnable;
MySimpleRunnable = nullptr;
}
}
FSimpleRunnable::FSimpleRunnable(const FString& ThreadName)
{
// 获取 FEvent 指针
ThreadEvent = FPlatformProcess::GetSynchEventFromPool();
// 创建线程实例
m_ThreadName = ThreadName;
ThreadIns = FRunnableThread::Create(this, *m_ThreadName, 0, TPri_Normal);
m_ThreadID = ThreadIns->GetThreadID();
UE_LOG(LogTemp, Warning, TEXT("Thread Start! ThreadID = %d"), m_ThreadID);
}
FSimpleRunnable::~FSimpleRunnable()
{
if (ThreadEvent) // 清空 FEvent*
{
FPlatformProcess::ReturnSynchEventToPool(ThreadEvent); // delete ThreadEvent;
ThreadEvent = nullptr;
}
if (ThreadIns) // 清空 FRunnableThread*
{
delete ThreadIns;
ThreadIns = nullptr;
}
}
void FSimpleRunnable::PauseThread()
{
bPause = true;
UE_LOG(LogTemp, Warning, TEXT("Thread Pause!"));
}
void FSimpleRunnable::WakeUpThread()
{
bRun = true;
bPause = false;
if (ThreadEvent)
{
ThreadEvent->Trigger(); // 唤醒线程
}
UE_LOG(LogTemp, Warning, TEXT("Thread Wakeup!"));
}
void FSimpleRunnable::Suspend(bool bSuspend)
{
if (ThreadIns)
{
ThreadIns->Suspend(bSuspend); //挂起/唤醒
}
}
void FSimpleRunnable::StopThread()
{
Stop();
}
void FSimpleRunnable::ShutDown(bool bShouldWait)
{
if (ThreadIns)
{
ThreadIns->Suspend(false);
ThreadIns->Kill(bShouldWait); // bShouldWait 为false,Suspend(true)时,会崩
}
}
bool FSimpleRunnable::Init()
{
return true;//若返回 false ,线程创建失败,不会执行后续函数
}
uint32 FSimpleRunnable::Run()
{
int32 count = 0;
FPlatformProcess::Sleep(0.03f); //延时,等待初始化完成
while (bRun)
{
if (bPause)
{
ThreadEvent->Wait(); // 线程挂起
if (!bRun) // 线程挂起时执行线程结束
{
return 0;
}
}
UE_LOG(LogTemp, Warning, TEXT("ThreadID: %d, Count: %d"), m_ThreadID, count);
count++;
FPlatformProcess::Sleep(0.1f); // 执行间隔,防止堵塞
}
return 0;
}
void FSimpleRunnable::Stop()
{
bRun = false;
bPause = false;
if (ThreadEvent)
{
ThreadEvent->Trigger(); // 保证线程不挂起
}
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, FString::Printf(TEXT("Stop")), true);
}
void FSimpleRunnable::Exit()
{
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, FString::Printf(TEXT("Exit")), true);
}
/// <summary>
/// TaskGraph
/// </summary>
void AAsyncLoadActor::CreateTaskGraphSimpleTask(int32 TotalCount)
{
/*
*第一个参数默认值为NULL
*第二个参数为线程逻辑运行的线程,上面已经介绍过
*ConstructAndDIspatchWhenReady会调用创建的TaskGraph类型(本例中为FTaskGraph)的构造函数,只需要按照构造
*函数参数列表传入参数即可完成TaskGraph的初始化
*/
TGraphTask<FTaskGraphTest>::CreateTask().ConstructAndDispatchWhenReady(TotalCount);
}
/// </summary>
/// <param name="path">地址</param>
/// <param name="OnAsyncLoadFinished">成功回调</param>
/// <param name="OnAsyncLoadFaile">失败回调</param>
void AAsyncLoadActor::MyAsyncLoad(FSoftObjectPath path, const FOnAsyncLoadLevel& OnAsyncLoadFinished, const FOnAsyncLoadFailed& OnAsyncLoadFaile)
{
//LoadPackagePath = FPaths::GetBaseFilename(LoadPackageSoftObjectPath.ToString(), false);
LoadPackagePath = FPaths::GetBaseFilename(path.ToString(), false);
bIsLoaded = false;
UE_LOG(LogTemp, Warning, TEXT("String: %s"), *LoadPackagePath);
//UE_LOG(LogTemp, Warning, TEXT("String2: %s"), *LoadPackagePath2);
LoadPackageAsync(
LoadPackagePath,
FLoadPackageAsyncDelegate::CreateLambda([=](const FName& PackageName, UPackage* LoadedPackage, EAsyncLoadingResult::Type Result)
{
if (Result == EAsyncLoadingResult::Failed)
{
UE_LOG(LogTemp, Warning, TEXT("Load Failed"));
OnAsyncLoadFaile.ExecuteIfBound();
}
else if (Result == EAsyncLoadingResult::Succeeded)
{
bIsLoaded = true;
UE_LOG(LogTemp, Warning, TEXT("Load Succeeded"));
OnAsyncLoadFinished.ExecuteIfBound();
}
}), 0, PKG_ContainsMap);
}
float AAsyncLoadActor::GetLoadProgress()
{
/// <summary>
/// 加载资产进度
/// </summary>
/// <returns>资产路径</returns>
float FloatPercentage = GetAsyncLoadPercentage(*LoadPackagePath);
if (!bIsLoaded)
{
FString ResultStr = FString::Printf(TEXT("Percentage: %f"), FloatPercentage);
GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, ResultStr);
UE_LOG(LogTemp, Warning, TEXT("Percentage: %f"), FloatPercentage);
}
else {
FloatPercentage = 100;
}
return FloatPercentage;
}
4.
.h
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AsyncLoadFunctionLibrary.generated.h"
DECLARE_DYNAMIC_DELEGATE_OneParam(FAsyncTaskDelegate, int, IntOut);
/**
* https://zhuanlan.zhihu.com/p/430187330
*/
UCLASS()
class EDITORTOOLSDEMO_API UAsyncLoadFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
/// <summary>
/// 使用AsyncTask
/// </summary>
UFUNCTION(BlueprintCallable, Category = "AsyncLoadTest")
static void AsyncPrimeCount(FAsyncTaskDelegate Out, int32 PrimeCount);
};
.cpp
#include "AsyncLoad/AsyncLoadFunctionLibrary.h"
void UAsyncLoadFunctionLibrary::AsyncPrimeCount(FAsyncTaskDelegate Out, int32 PrimeCount)
{
// Schedule a thread // Pass in our parameters to the lambda expression
AsyncTask(ENamedThreads::AnyHiPriThreadNormalTask, [Out, PrimeCount]()
{
//整个代码只是循环数字并计算它找到了多少质数
int PrimesFound = 0;
int TestNumber = 2;
/* while (PrimesFound< PrimeCount)
{
TestNumber++;
Out.ExecuteIfBound(TestNumber);
/// <summary>
/// 等待0.1秒再执行下一次
/// </summary>
FPlatformProcess::Sleep(0.1f);
}
return;*/
while (PrimesFound < PrimeCount)
{
bool bIsPrime = true;
for (int i = 2; i < TestNumber / 2; i++)
{
if (TestNumber % i == 0)
{
bIsPrime = false;
break;
}
}
if (bIsPrime)
{
PrimesFound++;
// 每1000个质数
if (PrimesFound % 1000 == 0)
{
//回到主线程并传递参数
AsyncTask(ENamedThreads::GameThread, [Out, PrimesFound]()
{
// 执行委托和参数
Out.ExecuteIfBound(PrimesFound);
});
}
}
TestNumber++;
}
});
}