UE在启动时涉及多个线程,这些线程共同协作以完成引擎的初始化和准备工作。
一、线程类型
-
标准多线程实现(FRunnable)
- 虚幻引擎对各个平台线程实现进行了封装,抽象出了FRunnable,类似于Java中的多线程方式。FRunnableThreadc负责创建多线程,它持有FRunnable的实例。
-
使用线程池的AsyncTask
- AsyncTask是基于虚幻线程池线程实现的异步任务系统,它使用线程池内的空闲线程来执行逻辑。AsyncTask系统包括FNonAbandonableTask(逻辑的载体)以及FAsyncTask和FAutoDeleteAsyncTask(都用来启动线程)。
-
TaskGraph
- TaskGraph是虚幻引擎提供的任务图系统,用于解决任务间的相互依赖关系。它需要自己实现逻辑载体类和启动线程的TGraphTask类。
二、具体线程
-
游戏线程(Game Thread)
- 游戏线程主要负责场景物体的逻辑计算。在引擎启动时,游戏线程会进行一系列的初始化工作,包括加载游戏资源、设置游戏参数等。
-
渲染线程(Render Thread)
- 渲染线程负责图元渲染指令的生成。在引擎启动时,渲染线程会进行渲染系统的初始化,包括设置渲染参数、创建渲染资源等。
-
RHI线程(RHI Thread)
- RHI线程负责将渲染指令提交至GPU。在引擎启动时,RHI线程会进行与图形硬件接口(RHI)相关的初始化工作,以确保渲染指令能够正确地提交给GPU。
-
其他线程
- 除了上述三个主要线程外,虚幻引擎在启动时还会创建其他线程,如Task线程(用于处理任务图系统中的任务)、线程池线程(用于执行AsyncTask中的异步任务)等。这些线程的数量和类型可能会根据引擎的配置和启动模式而有所不同。
三、线程初始化过程
-
预初始化
- 在引擎启动的预初始化阶段,会判断引擎的启动模式(如游戏模式或服务器模式),并加载核心模块(如CoreUObject)。
-
模块初始化
- 随后,引擎会启动所有的PreInitModules,这些模块包括引擎模块、渲染模块、动画蓝图、Slate渲染模块等。这些模块在加载完毕后,会进行各自的初始化工作。
-
AppInit函数调用
- 当所有模块加载并初始化完毕后,会调用AppInit函数,进入引擎正式的初始化阶段。在这一阶段,所有被加载到内存的模块(如果有PostEngineInit函数)都会被调用以完成最终的初始化工作。
UE调用多线程
一、创建FRunnable类:
class FMySimpleRunnable:public FRunnable
FRunnable类介绍:
“可运行”对象的接口。 可运行对象是在任意线程上“运行”的对象。调用使用模式为Init0、Run0、Exit0。 将要“运行”此对象的线程始终使用这些调用语义。它在创建线程时这样做,以便在这些调用的上下文中提供任何线程特定使用(TLS等)。“可运行”对象在Init0中执行所有初始化。 如果初始化失败,则线程停止执行并返回错误代码 如果成功,则调用 Run0,其中执行真正的线程工作。完成后,调用 Exit0 以进行正确的清理。
虚函数实现
//判断能否初始化
virtual bool Init() override;
//开始运行时
//在此写需要运行在线程上的任务
virtual uint32 Run() override;
//结束运行时
virtual void Exit() override;
//结束该线程,该任务是在外部调用放的线程上执行的
virtual void Stop() override;
二、实例化
//实例化可运行对象Runnable类
MySimpleRunnable =MakeShared<FMySimpleRunnable>(TEXT("MyThread No.1"));
三、调起线程,执行Runnable可执行类
//创建可运行线程的接口。
//此接口指定用于管理线程生存周期的方法
MySimpleThreadPtr=FRunnableThread::Create(MySimpleRunnable.Get(),*MySimpleRunnable->GetThreadName());
四、结束该线程
if (MySimpleRunnable.IsValid())
{
MySimpleRunnable->Stop();
}
五、让当前线程等待这个线程执行完,以防未执行完线程
if (MySimpleThreadPtr)
{
//让当前线程等待这个线程执行完
MySimpleThreadPtr->WaitForCompletion();
}
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;
}
void FRunnableThread::SetupCreatedThread(FRunnableThread*& NewThread, class FRunnable* InRunnable, const TCHAR* ThreadName, uint32 InStackSize, EThreadPriority InThreadPri, uint64 InThreadAffinityMask, EThreadCreateFlags InCreateFlags)
{
// Call the thread's create method
bool bIsValid = NewThread->CreateInternal(InRunnable, ThreadName, InStackSize, InThreadPri, InThreadAffinityMask, InCreateFlags);
if( bIsValid )
{
check(NewThread->Runnable);
NewThread->PostCreate(InThreadPri);
}
else
{
// We failed to start the thread correctly so clean up
delete NewThread;
NewThread = nullptr;
}
}
异步函数
void UMyThreadSubsystem::PringWaring(FString InString)
{
//异步执行线程 参数1为线程名称,参数2为任务
//需要用到哪个线程的上下文参数就传入哪个线程
AsyncTask(ENamedThreads::GameThread, [InString]()
{
UE_LOG(LogTemp, Warning, TEXT("ThreadLog:[%s]"), *InString);
});
}
//可选择调用线程方式
//新创建一个线程 比较消耗性能
Async(EAsyncExecution::Thread, []()
{
FPlatformProcess::Sleep(2);
});