[UE4]Multi-Thread Notes
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