An Introductory tutorial to multithreading in Unreal Engine. We discuss how to create multithreaded games in unreal engine, the thread’s lifecycle, and how to share data between the thread and the main game thread. To archive multithreading in Unreal Engine 5, we make use of the unreal FRunnable class and the FRunnableThread class.
How FRunnableThread and FRunnable Classes Work Together
A Thread in unreal engine is an object of the class FRunnableThread. So when you create a “thread” in unreal engine you will be creating an object from the FRunnableThread class.
Create an object of FRunnableThread
To create a thread in unreal engine, we call the static function FRunnableThread::Create(). This function requires a pointer to an object of class FRunnable:
What this means is that we have to create an object of FRunnable before we create a FRunnableThread object.
You can think of this FRunnable object as the controller of the FRunnableThread. A FRunnableThread requires a controller, a master if you will, that will control what it does, how it does it, and when to die. And this controller must be an object of FRunnable class or child class.
We will get back to this create() function and where to call it later. For now, we just wanted you to understand why we are about to create a FRunnable child class.
Create a pointer to FRunnable
To create an object of the unreal FRunnable class, we must first create a child class of it.
What we want is to create an object of this child class so that we can have control over the thread and what it does. In this tutorial we called this child class FMyRunnable. You need to override the following virtual methods from FRunnable:
#include "CoreMinimal.h"
class FMyRunnable : public FRunnable
{
public:
FMyRunnable();
//override Init,Run and Stop.
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Exit() override;
virtual void Stop() override;
}
Override the Init, Run, Exit and Stop functions
The functions we just overloaded are going to be called automatically by the FRunnableThread object.
Remember in the beginning we noted that FRunnableThread requires an object of FRunnable? Well, the reason it requires it, is because these functions are the four main control points of the thread. They are where unreal engine gives the developer a chance to determine a thread’s behavior during its lifetime.
They are callback functions that the FRunnableThread will bind to and call at different points of its lifetime to give you a chance to kill it or exchange data with the main thread. So basically you won’t need to call them yourself when you are multithreading in unreal engine.. You just need to define what should happen when they are called.
We will explain what logic is meant to go into these functions. Keep in mind these functions are for unreal engine to call, not for you to call directly. Keep reading this multithreading tutorial to understand how everything ties together and what functions you CAN and MUST call as the developer.
Where and When are these functions called
You may have noticed we have not yet covered the actual logic that should go into these functions as you write your multithreaded code in unreal engine.
This is because you need to understand WHERE and WHEN these virtual functions are called before you can decide what logic/implementation goes into which.
The Init() function.
Runs in the game thread. Blocking.
Controls What happens immediately after a thread is created. Consider this your initialization/constructor for your thread. If you return false in this function, your thread won’t run.
The Run() function.
Runs in the new thread. Nonblocking.
The actual function that does the multithreaded work, is the RUN() function.
You can think of the run() function as the equivalent of a main() function but for your thread. It returns an int just like basic c++ programs and should return 0 if successful.
Any code that runs in this function will not block your game thread or eat into your fps(in theory). An exception to this is if you call this function manually from your game thread. Then it will run in your game thread. Do not call these functions manually. In simplified terms, this function hosts the code that should run in the new thread.
The Exit() function.
Runs in the new thread. Nonblocking. What Happens immediately after the thread is done running. i.e After the Run() Function exits. Maybe you want to do some clean-up.
The Stop() function.
Runs in the game thread. Blocking.
This function will run after FRunnableThread::Kill() is called. It will run AT THE SAME TIME as your Run() function but in different threads. The idea is it will write to a bool that Run() will check, and then Run() will exit its run loop. Usually a while loop.
Normally a thread will exit() on its own when the run function is done but sometimes you may wish to kill it early.
This is the function to put your code that will signal to the run() function;
“hey, the developer has requested this thread to forcefully exit, so stop loop execution and head to the exit”.
Keep in mind that the EXIT() runs after the RUN() has exited. So the stop() function runs, writing to a bool, and the RUN() function reads the bool. it will then exit, and then the Exit() function will run. . You do not need to call KILL on a thread that has already exited or finished normally.
The Workflow of multithreading in unreal engine
We have covered the where, as in which thread and now it’s time you understand when these functions are called. We will give you a high-level overview of the process so that you can start multithreading in unreal engine as soon as possible. Here is the calling order:
- You call FRunnableThread::Create()
- FRunnableThread::Create will then call your FRunnable::Init()
- If your FRunnable::Init() returns true, The FRunnable::Run() function will be called.
- The FRunnable::Run() will execute. You are now officially running in a separate thread. This function will immediately exit when it’s done. You can use a while loop to keep it running. We’ll explain this later.
- The FRunnable::Exit() will execute immediately after the Run() function exits.
- The FRunnable::Stop will only get called when FRunnableThread::Kill() is called. The Kill function will automatically call your FRunnable::Stop function. We will explain where to call these functions later.
At this point, you should have a high level understanding of multithreading in unreal engine and the workflow. That’s it for now. If you’re a visual learner , we highly recommend our Unreal Engine Multithreading For Beginners Course. That’s it for now and we hope we shed some light on what you need to do.