Keywords: UE4, Multi-Thread Notes

How to create thread

Origin: UnrealEngine official wiki
Example
Header:

//~~~~~ Multi Threading ~~~
class FPrimeNumberWorker : public FRunnable
{    
    /** Singleton instance, can access the thread any time via static accessor, if it is active! */
    static  FPrimeNumberWorker* Runnable;
 
    /** Thread to run the worker FRunnable on */
    FRunnableThread* Thread;
 
    /** The Data Ptr */
    TArray<uint32>* PrimeNumbers;
 
    /** The PC */
    AVictoryGamePlayerController* ThePC;
 
    /** Stop this thread? Uses Thread Safe Counter */
    FThreadSafeCounter StopTaskCounter;
 
    //The actual finding of prime numbers
    int32 FindNextPrimeNumber();
 
private:
    int32                PrimesFoundCount;
public:
 
    int32                TotalPrimesToFind;
 
    //Done?
    bool IsFinished() const
    {
        return PrimesFoundCount >= TotalPrimesToFind;
    }
 
    //~~~ Thread Core Functions ~~~
 
    //Constructor / Destructor
    FPrimeNumberWorker(TArray<uint32>& TheArray, const int32 IN_PrimesToFindPerTick, AVictoryGamePlayerController* IN_PC);
    virtual ~FPrimeNumberWorker();
 
    // 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, AVictoryGamePlayerController* IN_PC);
 
    /** Shuts down the thread. Static so it can easily be called from outside the thread context */
    static void Shutdown();
 
    static bool IsThreadFinished();
};

CPP:

//***********************************************************
//Thread Worker Starts as NULL, prior to being instanced
//        This line is essential! Compiler error without it
FPrimeNumberWorker* FPrimeNumberWorker::Runnable = NULL;
//***********************************************************
 
FPrimeNumberWorker::FPrimeNumberWorker(TArray<uint32>& TheArray, const int32 IN_TotalPrimesToFind, AVictoryGamePlayerController* IN_PC)
    : ThePC(IN_PC)
    , TotalPrimesToFind(IN_TotalPrimesToFind)
    , 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;
}
 
//Init
bool FPrimeNumberWorker::Init()
{
    //Init the Data 
    PrimeNumbers->Empty();
    PrimeNumbers->Add(2);
    PrimeNumbers->Add(3);
 
    if(ThePC) 
    {
        ThePC->ClientMessage("**********************************");
        ThePC->ClientMessage("Prime Number Thread Started!");
        ThePC->ClientMessage("**********************************");
    }
    return true;
}
 
//Run
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++;
 
        //***************************************
        //Show Incremental Results in Main Game Thread!
 
        //    Please note you should not create, destroy, or modify UObjects here.
        //      Do those sort of things after all thread are completed.
 
        //      All calcs for making stuff can be done in the threads
        //         But the actual making/modifying of the UObjects should be done in main game thread.
        ThePC->ClientMessage(FString::FromInt(PrimeNumbers->Last()));
        //***************************************
 
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //prevent thread from using too many resources
        //FPlatformProcess::Sleep(0.01);
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    }
 
    //Run FPrimeNumberWorker::Shutdown() from the timer in Game Thread that is watching
        //to see when FPrimeNumberWorker::IsThreadFinished()
 
    return 0;
}
 
//stop
void FPrimeNumberWorker::Stop()
{
    StopTaskCounter.Increment();
}
 
FPrimeNumberWorker* FPrimeNumberWorker::JoyInit(TArray<uint32>& TheArray, const int32 IN_TotalPrimesToFind, AVictoryGamePlayerController* IN_PC)
{
    //Create new instance of thread if it does not exist
    //        and the platform supports multi threading!
    if (!Runnable && FPlatformProcess::SupportsMultithreading())
    {
        Runnable = new FPrimeNumberWorker(TheArray,IN_TotalPrimesToFind,IN_PC);            
    }
    return Runnable;
}
 
void FPrimeNumberWorker::EnsureCompletion()
{
    Stop();
    Thread->WaitForCompletion();
}
 
void FPrimeNumberWorker::Shutdown()
{
    if (Runnable)
    {
        Runnable->EnsureCompletion();
        delete Runnable;
        Runnable = NULL;
    }
}
 
bool FPrimeNumberWorker::IsThreadFinished()
{
    if(Runnable) return Runnable->IsFinished();
    return true;
}
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;
}

Start thread:

//player controller .cpp
//Multi-threading, returns handle that could be cached.
//        use static function FPrimeNumberWorker::Shutdown() if necessary
FPrimeNumberWorker::JoyInit(PrimeNumbers, 50000, this);

Stop thread:

void FPrimeNumberWorker::EnsureCompletion()  
{  
    Stop();  
    Thread->WaitForCompletion();  
}
How to get current thread name
uint32 ThreadId = FPlatformTLS::GetCurrentThreadId();
FString ThreadName = FThreadManager::Get().GetThreadName(ThreadId);

“The most serious mistakes are not being made as a result of wrong answers. The true dangerous thing is asking the wrong question.” ― Peter Drucker