虚幻引擎编程基础(二)
文章目录
一、前言
在 虚幻引擎编程基础(一) 中整理一些基础的数据结构用法。
本文主要会继续简单地整理一些相关的基础内容,如多线程,垃圾回收,以及一些常用的代码块。
以下是笔者的一些笔记。如有错误,还请见谅。
二、多线程
线程是操作系统能够进行运行调度的最小单位。
一个进程中可以并发多个线程,每条线程并行执行不同的任务。
游戏引擎中,多线程用的一点也不少,比如渲染模块、物理模块、网络通信、音频系统、IO等。
UE4使用多线程的方式非常丰富,可以分为下面三类:
- 标准多线程实现FRunnable;
- 使用线程池的AsyncTask;
- TaskGraph;
2.1 FRunnable & FRunnableThread
UE4是跨平台的引擎,对各个平台线程实现进行了封装,抽象出了 FRunnable,类似与Java中的多线程方式。
主要需要认识的类有:
FRunnable
FRunnableThread
其中:
FRunnable是需要继承实现的线程执行体
。其接口如下:
class CORE_API FRunnable
{
public:
/**
* Initializes the runnable object.
*
* This method is called in the context of the thread object that aggregates this, not the
* thread that passes this runnable to a new thread.
*
* @return True if initialization was successful, false otherwise
* @see Run, Stop, Exit
*/
virtual bool Init()
{
return true;
}
/**
* Runs the runnable object.
*
* This is where all per object thread work is done. This is only called if the initialization was successful.
*
* @return The exit code of the runnable object
* @see Init, Stop, Exit
*/
virtual uint32 Run() = 0;
/**
* Stops the runnable object.
*
* This is called if a thread is requested to terminate early.
* @see Init, Run, Exit
*/
virtual void Stop() { }
/**
* Exits the runnable object.
*
* Called in the context of the aggregating thread to perform any cleanup.
* @see Init, Run, Stop
*/
virtual void Exit() { }
/**
* Gets single thread interface pointer used for ticking this runnable when multi-threading is disabled.
* If the interface is not implemented, this runnable will not be ticked when FPlatformProcess::SupportsMultithreading() is false.
*
* @return Pointer to the single thread interface or nullptr if not implemented.
*/
virtual class FSingleThreadRunnable* GetSingleThreadInterface( )
{
return nullptr;
}
/** Virtual destructor */
virtual ~FRunnable() { }
};
FRunnableThreadc,才是真正负责创建的多线程,它持有FRunnable的实例。
- 通过FRunnableThread::Create创建相应平台的线程。
FRunnableThread* FRunnableThread::Create(
class FRunnable* InRunnable,
const TCHAR* ThreadName,
uint32 InStackSize,
EThreadPriority InThreadPri,
uint64 InThreadAffinityMask,
EThreadCreateFlags InCreateFlags)
{
bool bCreateRealThread = FPlatformProcess::SupportsMultithreading();
FRunnableThread* NewThread = nullptr;
if (bCreateRealThread)
{
check(InRunnable);
// Create a new thread object
NewThread = FPlatformProcess::CreateRunnableThread();
}
else if (InRunnable->GetSingleThreadInterface())
{
// Create a fake thread when multithreading is disabled.
NewThread = new FFakeThread();
}
if (NewThread)
{
SetupCreatedThread(NewThread, InRunnable, ThreadName, InStackSize, InThreadPri, InThreadAffinityMask, InCreateFlags);
}
return NewThread;
}
例如Windows平台:
- FRunnableThread::SetupCreatedThread函数中,会调用NewThread->CreateInternal,从而调用FRunnableThreadWin的CreateInternal,进行Windows的线程创建;
// Create the new thread
{
LLM_SCOPE(ELLMTag::ThreadStack);
LLM_PLATFORM_SCOPE(ELLMTag::ThreadStackPlatform);
// add in the thread size, since it's allocated in a black box we can't track
LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Default, nullptr, InStackSize));
LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Platform, nullptr, InStackSize));
// Create the thread as suspended, so we can ensure ThreadId is initialized and the thread manager knows about the thread before it runs.
Thread = CreateThread(NULL, InStackSize, _ThreadProc, this, STACK_SIZE_PARAM_IS_A_RESERVATION | CREATE_SUSPENDED, (::DWORD *)&ThreadID);
}
- 在传入的_ThreadProc函数,会调用GuardedRun方法;
/**
* The thread entry point. Simply forwards the call on to the right
* thread main function
*/
static ::DWORD STDCALL _ThreadProc( LPVOID pThis )
{
check(pThis);
auto* ThisThread = (FRunnableThreadWin*)pThis;
FThreadManager::Get().AddThread(ThisThread->GetThreadID(), ThisThread);
return ThisThread->GuardedRun();
}
- GuardedRun方法中调用Run,从而运行FRunnable::Run(线性执行体)。
uint32 FRunnableThreadWin::Run()
{
// Assume we'll fail init
uint32 ExitCode = 1;
check(Runnable);
// Initialize the runnable object
if (Runnable->Init() == true)
{
// Initialization has completed, release the sync event
ThreadInitSyncEvent->Trigger();
// Setup TLS for this thread, used by FTlsAutoCleanup objects.
SetTls();
// Now run the task that needs to be done
ExitCode = Runnable->Run();
// Allow any allocated resources to be cleaned up
Runnable->Exit();
#if STATS
FThreadStats::Shutdown();
#endif
FreeTls();
}
else
{
// Initialization has failed, release the sync event
ThreadInitSyncEvent->Trigger();
}
return ExitCode;
}
以上就是Unreal自带最基础的多线程的实现方式。
渲染线程的创建就是使用的这种方法。
在LauchEngineLoop.cpp
的FEngineLoop::PreInitPreStartupScreen
函数中会调用StartRenderingThread
函数:
// Turn on the threaded rendering flag.
GIsThreadedRendering = true;
// Create the rendering thread.
// 创建渲染线程
GRenderingThreadRunnable = new FRenderingThread();
Trace::ThreadGroupBegin(TEXT("Render"));
PRAGMA_DISABLE_DEPRECATION_WARNINGS
GRenderingThread =
PRAGMA_ENABLE_DEPRECATION_WARNINGS
FRunnableThread::Create(GRenderingThreadRunnable,
*BuildRenderingThreadName(ThreadCount), 0,
FPlatformAffinity::GetRenderingThreadPriority(),
FPla