【转载】UE4 多线程代码片段

680 阅读2分钟

版权声明:本文为CSDN博主「wblong_cs」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:blog.csdn.net/mrbaolong/a…

FNullGraphTask

#include "Async/TaskGraphInterfaces.h"
#include "Async/ParallelFor.h"

FString string;
double StartTime = FPlatformTime::Seconds();
FGraphEventArray Tasks;
Tasks.AddZeroed(10);
ParallelFor(10, [&Tasks](int32 Index)
            {
                FGraphEventArray InnerTasks;
                InnerTasks.AddZeroed(100);
                ENamedThreads::Type CurrentThread = FTaskGraphInterface::Get().GetCurrentThreadIfKnown();
                for (int32 InnerIndex = 0; InnerIndex < 100; InnerIndex++)
                {
                    InnerTasks[InnerIndex] = TGraphTask<FNullGraphTask>::CreateTask(nullptr, CurrentThread).ConstructAndDispatchWhenReady(TStatId(), ENamedThreads::AnyThread);
                }
                Tasks[Index] = TGraphTask<FNullGraphTask>::CreateTask(&InnerTasks, ENamedThreads::GameThread).ConstructAndDispatchWhenReady(TStatId(), ENamedThreads::AnyThread);
            });
            
double QueueTime = FPlatformTime::Seconds();
string = FString::Printf(TEXT("Tasks QueueTime: %f."), QueueTime - StartTime);
UE_LOG(LogTemp, Warning, TEXT("%s"), *string);

FGraphEventRef Join = TGraphTask<FNullGraphTask>::CreateTask(&Tasks, ENamedThreads::GameThread).ConstructAndDispatchWhenReady(TStatId(), ENamedThreads::AnyThread);
double JoinTime = FPlatformTime::Seconds();
string = FString::Printf(TEXT("Tasks JoinTime: %f."), JoinTime - QueueTime);
UE_LOG(LogTemp, Warning, TEXT("%s"), *string);

FTaskGraphInterface::Get().WaitUntilTaskCompletes(Join, ENamedThreads::GameThread_Local);
double EndTime = FPlatformTime::Seconds();
string = FString::Printf(TEXT("Tasks EndTime: %f."), EndTime - JoinTime);
UE_LOG(LogTemp, Warning, TEXT("%s"), *string);

输出

LogTemp: Warning: Tasks QueueTime: 0.000470.
LogTemp: Warning: Tasks JoinTime:  0.000152.
LogTemp: Warning: Tasks EndTime:   0.000085.

AsyncTask

以找素数的任务为例

PrimeCalculationAsyncTask.hpp

#include "Async/AsyncWork.h"

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()
    {
        //TODO:具体耗时的任务
    }
};

实际使用

//完成后自动删除操作
 auto task = new FAutoDeleteAsyncTask<PrimeCalculationAsyncTask>(MaxPrime);
 if (task)
 {
     task -> StartBackgroundTask();
 } 

FRunnable

以找素数的线程为例

PrimeNumberWorker.h

class FPrimeNumberWorker : public FRunnable
{
    /** Singleton instance, can access the thread any time via static accessor, if it is active! */
    static FPrimeNumberWorker *CRunnable;

    /** Thread to run the worker FRunnable on */
    FRunnableThread *Thread;

    /** The Data Ptr */
    TArray<uint32> *PrimeNumbers;

    /** Stop this thread? Uses Thread Safe Counter */
    FThreadSafeCounter StopTaskCounter;
    //The actual finding of prime numbers
    int32 FindNextPrimeNumber();

private:
    int32 PrimesFoundCount;
    //Constructor / Destructor
    FPrimeNumberWorker(TArray<uint32> &TheArray, const int32 IN_PrimesToFindPerTick);
    virtual ~FPrimeNumberWorker();

public:
    int32 TotalPrimesToFind;

    //Done?
    bool IsFinished() const
    {
        return PrimesFoundCount >= TotalPrimesToFind;
    }

    //~~~ Thread Core Functions ~~~

    // Begin FRunnable interface.
    virtual bool Init();
    virtual uint32 Run();
    virtual void Stop();
    // End FRunnable interface

    /** Makes sure this thread has stopped properly */
    void EnsureCompletion();
    //~~~ Starting and Stopping Thread ~~~

    /*
		Start the thread and the worker from static (easy access)!
		This code ensures only 1 Prime Number thread will be able to run at a time.
		This function returns a handle to the newly started instance.
	*/
    static FPrimeNumberWorker *JoyInit(TArray<uint32> &TheArray, const int32 IN_TotalPrimesToFind);
    /** Shuts down the thread. Static so it can easily be called from outside the thread context */
    static void Shutdown();
    static bool IsThreadFinished();
};

PrimeNumberWorker.cpp

#include "PrimeNumberWorker.h"

int32 FPrimeNumberWorker::FindNextPrimeNumber()
{
    //Last known prime number  + 1
    int32 TestPrime = PrimeNumbers->Last();

    bool NumIsPrime = false;

    while (!NumIsPrime)
    {
        NumIsPrime = true;

        //Try Next Number
        TestPrime++;

        //Modulus from 2 to current number - 1
        for (int32 b = 2; b < TestPrime; b++)
        {
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            //prevent thread from using too many resources
            //FPlatformProcess::Sleep(0.01);
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

            if (TestPrime % b == 0)
            {
                NumIsPrime = false;
                break;
                //~~~
            }
        }
    }

    //Success!
    return TestPrime;
}

FPrimeNumberWorker::FPrimeNumberWorker(TArray<uint32> &TheArray, const int32 IN_PrimesToFindPerTick) : TotalPrimesToFind(IN_PrimesToFindPerTick), StopTaskCounter(0), PrimesFoundCount(0)
{
    //Link to where data should be stored
    PrimeNumbers = &TheArray;

    Thread = FRunnableThread::Create(this, TEXT("FPrimeNumberWorker"), 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more
}

FPrimeNumberWorker::~FPrimeNumberWorker()
{
    delete Thread;
    Thread = NULL;
}

bool FPrimeNumberWorker::Init()
{
    PrimeNumbers->Empty();
    PrimeNumbers->Add(2);
    PrimeNumbers->Add(3);
    return true;
}

uint32 FPrimeNumberWorker::Run()
{
    //Initial wait before starting
    FPlatformProcess::Sleep(0.03);

    //While not told to stop this thread
    //		and not yet finished finding Prime Numbers
    while (StopTaskCounter.GetValue() == 0 && !IsFinished())
    {
        PrimeNumbers->Add(FindNextPrimeNumber());
        PrimesFoundCount++;

        FPlatformProcess::Sleep(0.01);
    }
    return 0;
}

void FPrimeNumberWorker::Stop()
{
    StopTaskCounter.Increment();
}

void FPrimeNumberWorker::EnsureCompletion()
{
    Stop();
    Thread->WaitForCompletion();
}

FPrimeNumberWorker *FPrimeNumberWorker::JoyInit(TArray<uint32> &TheArray, const int32 IN_TotalPrimesToFind)
{
    if (!CRunnable && FPlatformProcess::SupportsMultithreading())
    {
        CRunnable = new FPrimeNumberWorker(TheArray, IN_TotalPrimesToFind);
    }
    return CRunnable;
}

void FPrimeNumberWorker::Shutdown()
{
    if (CRunnable)
    {
        CRunnable->EnsureCompletion();
        delete CRunnable;
        CRunnable = NULL;
    }
}

bool FPrimeNumberWorker::IsThreadFinished()
{
    if (CRunnable)
        return CRunnable->IsFinished();
    return true;
}