这篇博客介绍了UE4中多线程任务的C++构建。
在游戏的主线程中,如果存在一个很复杂的运算,那么游戏很容易造成卡顿。在这篇博客里,我使用查找第N个质数的函数作为范例,并且分别创建了一个在游戏主进程以及新开线程中的调用。
函数构建
新建一个AActor,加入如下函数:
// Header file.
protected:
/* Calculates prime numbers in the game thread */
UFUNCTION(BlueprintCallable, Category = MultiThreading)
void CalculatePrimeNumbers();
/* Calculates prime numbers in a background thread */
UFUNCTION(BlueprintCallable, Category = MultiThreading)
void CalculatePrimeNumbersAsync();
/* The max prime number */
UPROPERTY(EditAnywhere, Category = MultiThreading)
int32 MaxPrime;
新建一个FuntionLibrary,在其中加入CalculatePrimeNumbers
函数,用于计算结果:
// MainFunctionLibrary.cpp
void UMainFunctionLibrary::CalculatePrimeNumbers(int32 UpperLimit)
{
//Calculating the prime numbers...
for (int32 i = 1; i <= UpperLimit; i++)
{
bool isPrime = true;
for (int32 j = 2; j <= i / 2; j++)
{
if (FMath::Fmod(i, j) == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
GLog->Log("Prime number #" + FString::FromInt(i) + ": " + FString::FromInt(i));
}
}
接着在Actor中添加CalculatePrimeNumbers
函数:
void AMultiThreadingActor::CalculatePrimeNumbers()
{
//Performing the prime numbers calculations in the game thread...
UMainFunctionLibrary::CalculatePrimeNumbers(MaxPrime);
}
并且添加一个CalculatePrimeNumbersAsync
函数的空实现。在编译通过之后,在Actor的继承BP中添加操作如下:
Task的创建
提到多线程就一定是Tasks。在这里我们创建一个在另一个线程中执行CalculatePrimeNumbers函数的类。
在这里我们需要新添加一个类继承自FNonAbandonableTask
的类(可以阅读引擎源代码查看该类的详细信息)。
class PrimeCalculationAsyncTask : public FNonAbandonableTask
{
int32 MaxPrime;
public:
/*Default constructor*/
PrimeCalculationAsyncTask(int32 MaxPrime)
{
this->MaxPrime = MaxPrime;
}
/*This function is needed from the API of the engine.
My guess is that it provides necessary information
about the thread that we occupy and the progress of our task*/
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(PrimeCalculationAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
}
/*This function is executed when we tell our task to execute*/
void DoWork()
{
UMainFunctionLibrary::CalculatePrimeNumbers(MaxPrime);
}
};
RETURN_QUICK_DECLARE_CYCLE_STAT
宏函数就是用于处理线程池相关的信息,而DoWork函数则是进行函数的调用。
因此在这里就可以在CalculatePrimeNumbersAsync
函数中来进行调用了。需要注意的是我们需要创建一个FAutoDeleteAsyncTask
来进行对应的操作,它能够保证在task完成后把自己删除。
void AMultiThreadingActor::CalculatePrimeNumbersAsync()
{
auto task = new FAutoDeleteAsyncTask<PrimeCalculationAsyncTask>(MaxPrime);
if (task)
task -> StartBackgroundTask();
}
如果读者想要了解更多关于UE4中的多线程操作,可以从源代码中的AsyncWork.h开始读起。
<全文完>