UE4多線程概述


為了提升游戲的運行幀率,減少卡頓,UE4中使用了大量的線程來提升游戲的並發程度,來釋放GamePlay游戲線程的壓力。

 

具體包括:

① 將渲染的應用程序階段的工作放在RenderThread中

② 將渲染命令提交放在RHIThread中

③ 將Actor及ActorComponent的Tick、GC Mark等放到TaskGraph中並行化

④ GC Sweep的內存釋放邏輯放在FAsyncPurge線程中

⑤ 資源放到AsyncLoading線程中異步加載

⑥ 物理模擬會在PhysX內部的線程中計算

⑦ 聲音放在AudioThread線程中

⑧ Stat統計數據的收集放到StatThread線程中

⑨ 在FAsyncWriter線程中寫log到文件

 

#include "CoreMinimal.h"

DEFINE_LOG_CATEGORY_STATIC(TestLog, Log, All);

IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMultiThreadTest, "MyTest1.PublicTest.MultiThreadTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter) // 可被Automation System識別 class FTest1Runable : public FRunnable
{
public:
    FTest1Runable(int32 index)
    {
        ThreadIndex = index;
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Contruct: ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
    }

    virtual ~FTest1Runable() override
    {
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Destruct: ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
    }

    virtual bool Init() override   // 線程創建后,執行初始化工作   【在線程上執行】
    {
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Init: ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
        return true;
    }
    virtual uint32 Run() override   // 放置線程運行的代碼   【在線程上執行】
    {
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Run: 111 ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
        
        // FPlatformProcess::Sleep(xx)可讓當前線程Suspend xx秒   注:其他線程不受影響
        switch (ThreadIndex)
        {
        case 0:
            FPlatformProcess::Sleep(20.0f);
        default:
            FPlatformProcess::Sleep(1.0f);
            break;
        }
        
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Run: 222 ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
        FPlatformProcess::Sleep(2.0f);
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Run: 333 ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
        return 0;
    }
    virtual void Stop() override
    {
    }

    virtual void Exit() override   // 線程退出之前,進行清理工作   【在線程上執行】
    {
        UE_LOG(TestLog, Log, TEXT("FTest1Runable Exit: ThreadIndex:%d tid:0x%x FrameIndex:%llu"), ThreadIndex, FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
    }

private:
    int32 ThreadIndex;
};


bool FMultiThreadTest::RunTest(const FString& Parameters)
{
    UE_LOG(TestLog, Log, TEXT("RunTest Begin tid:0x%x FrameIndex:%llu"), FPlatformTLS::GetCurrentThreadId(), GFrameCounter);

    // 調用FRunnableThread::Create來創建一個線程
    FRunnableThread* Test1Thread0 = FRunnableThread::Create(new FTest1Runable(0), TEXT("Test1Thread0"));  // Test1Thread0為線程名
    FRunnableThread* Test1Thread1 = FRunnableThread::Create(new FTest1Runable(1), TEXT("Test1Thread1"));
    FRunnableThread* Test1Thread2 = FRunnableThread::Create(new FTest1Runable(2), TEXT("Test1Thread2"));

    UE_LOG(TestLog, Log, TEXT("RunTest End tid:0x%x FrameIndex:%llu"), FPlatformTLS::GetCurrentThreadId(), GFrameCounter);
    return true;
}

 

Automation System(自動化測試系統)  菜單:Window -- Developer Tools -- Session Frontend

 

 

 

執行流程解釋如下:

// 游戲線程 ID:0xe08     Test1Thread0 ID: 0xa67c     Test1Thread1 ID: 0x7104     Test1Thread2 ID: 0xb294

// 在第6390幀  游戲線程調用FRunnableThread::Create創造出Test1Thread0、Test1Thread1、Test1Thread2
//            各個線程被創建出來后,在自己的線程中立即調用了Init和Run方法
[2020.09.28-07.11.59:411][390]TestLog: RunTest Begin tid:0xe08 FrameIndex:6390
[2020.09.28-07.11.59:411][390]TestLog: FTest1Runable Contruct: ThreadIndex:0 tid:0xe08 FrameIndex:6390
[2020.09.28-07.11.59:412][390]TestLog: FTest1Runable Init: ThreadIndex:0 tid:0xa67c FrameIndex:6390
[2020.09.28-07.11.59:412][390]TestLog: FTest1Runable Run: 111 ThreadIndex:0 tid:0xa67c FrameIndex:6390
[2020.09.28-07.11.59:412][390]TestLog: FTest1Runable Contruct: ThreadIndex:1 tid:0xe08 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: FTest1Runable Init: ThreadIndex:1 tid:0x7104 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: FTest1Runable Run: 111 ThreadIndex:1 tid:0x7104 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: FTest1Runable Contruct: ThreadIndex:2 tid:0xe08 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: FTest1Runable Init: ThreadIndex:2 tid:0xb294 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: FTest1Runable Run: 111 ThreadIndex:2 tid:0xb294 FrameIndex:6390
[2020.09.28-07.11.59:413][390]TestLog: RunTest End tid:0xe08 FrameIndex:6390

// 在第6474幀
// 過了1s,線程Test1Thread1、Test1Thread2執行到Run 222處
[2020.09.28-07.12.00:418][475]TestLog: FTest1Runable Run: 222 ThreadIndex:1 tid:0x7104 FrameIndex:6474
[2020.09.28-07.12.00:418][475]TestLog: FTest1Runable Run: 222 ThreadIndex:2 tid:0xb294 FrameIndex:6474


// 在第6690幀
// 再過了2s,線程Test1Thread1、Test1Thread2退出Run函數,並調用Exit函數,線程的生命周期結束
[2020.09.28-07.12.02:416][691]TestLog: FTest1Runable Run: 333 ThreadIndex:2 tid:0xb294 FrameIndex:6690
[2020.09.28-07.12.02:416][691]TestLog: FTest1Runable Exit: ThreadIndex:2 tid:0xb294 FrameIndex:6690
[2020.09.28-07.12.02:416][691]TestLog: FTest1Runable Run: 333 ThreadIndex:1 tid:0x7104 FrameIndex:6690
[2020.09.28-07.12.02:416][691]TestLog: FTest1Runable Exit: ThreadIndex:1 tid:0x7104 FrameIndex:6690

// 在第8832幀
// 過了10s,線程Test1Thread0執行到Run 222處
[2020.09.28-07.12.20:419][833]TestLog: FTest1Runable Run: 222 ThreadIndex:0 tid:0xa67c FrameIndex:8832

// 在第9072幀
// 再過了2s,線程Test1Thread0退出Run函數,並調用Exit函數,線程的生命周期結束
[2020.09.28-07.12.22:419][ 73]TestLog: FTest1Runable Run: 333 ThreadIndex:0 tid:0xa67c FrameIndex:9072
[2020.09.28-07.12.22:419][ 73]TestLog: FTest1Runable Exit: ThreadIndex:0 tid:0xa67c FrameIndex:9072

 

如果想在單線程運行模式下(帶上-nothreading參數),以假線程FFakeThread的方式在游戲線程上來模擬執行的話,需要從FSingleThreadRunnable派生,並實現其GetSingleThreadInterface接口中返回當前this指針

class FStatsThread
    : public FRunnable
    , private FSingleThreadRunnable
{
// ... ...
public:
    virtual FSingleThreadRunnable* GetSingleThreadInterface() override
    {
        return this;
    }
};

 

 

 

1. FRunnable是線程可執行實體,其Run()函數為線程函數。Run()函數執行完,線程的生命周期也將結束

2. FRunnableThread是所有線程的基類,FRunnable為其成員變量

3. FRunnableThread相當於一個“外殼”,根據平台會創建出屬於那個平台的線程。而FRunnable是“核”,定義了這個線程具體要做什么

4. FThreadManager是全局的線程管理單例(通過靜態函數FThreadManager::Get()得到單例),可獲取到當前運行的所有線程

5. FRunnableThreadWin用於windows平台,FRunnableThreadPThread(對pthread的封裝)用於Android、iOS、Mac、Linux等平台

6. FFakeThread用於單線程運行模式下(帶上-onethread參數),以假線程的方式在游戲線程上來模擬執行

7. FRunnableThread相關函數

   const uint32 GetThreadID() const; // 線程ID,唯一

   const FString & GetThreadName() const; // 線程名稱 可重復

   EThreadPriority GetThreadPriority();  // 獲取線程的優先級

   void SetThreadPriority( EThreadPriority NewPriority );  // 設置線程的優先級

   void Suspend( bool bShouldPause = true );   // bShouldPause為true時,pause線程;bShouldPause為false時,resume線程

   void WaitForCompletion();  // 阻塞並等待當前線程執行完畢

   bool Kill( bool bShouldWait = true );   // 會先執行 runnable 對象的Stop函數,然后根據 bShouldWait 參數決定是否等待線程執行完畢。如果不等待,則強制殺死線程,可能會造成內存泄漏

 

線程 平台 解釋

主線程

 All

相關的代碼在:UnrealEngine\Engine\Source\Runtime\Launch目錄中

 

windows:為游戲線程

線程函數為:LaunchWindows.cpp下的WinMain函數

mac:為游戲線程

線程函數為:INT32_MAIN_INT32_ARGC_TCHAR_ARGV,其實展開就是main

內部會調用到obj c的NSApp(系統提供的App對象)

具體應用能實現的就只有后面的Delegate,所以UE4實現了UE4AppDelegate

真正做初始化在applicationDidFinishLaunching函數中,然后調用runGameThread函數

Android:線程名為MainThread-UE4

使用java代碼來處理主消息循環

Splash Activity:Engine\Build\Android\Java\src\com\epicgames\ue4\SplashActivity.java

游戲Activity:Engine\Build\Android\Java\src\com\epicgames\ue4\GameActivity.java.template

iOS:線程名為Thread <n>  如:Thead 1

線程函數為LaunchIOS.cpp下的main函數,會調用到obj c的UIApplicationMain

具體實現在IOSAppDelegate.cpp的MainAppThread函數中

 

對於Android和iOS,GameThread並不是主線程。在接入第三方SDK,一般都會從app的主線程調用回調

這時如果直接調用UE4相關函數,很有可能發生不可預知的問題,可通過AsyncTask將這些操作放在GameThread中跑

AsyncTask(ENamedThreads::GameThread, [=]()
{
SpawnActor();
//一些操作 });

游戲線程(消耗高)

 All

Windows:線程名為Main Thread

線程函數為:LaunchWindows.cpp下的WinMain函數

Android:線程名為Thread-<n>  如:Thread-3

線程函數為LaunchAndroid.cpp下的android_main函數

iOS:線程名為Thread <n>  如:Thread 5

線程函數為IOSAppDelegate.cpp下的MainAppThread函數

 

線程id:uint32 GGameThreadId

會被加入到TaskGraph系統中:FTaskGraphInterface::Get().AttachToThread(ENamedThreads::GameThread)

 

bool IsInGameThread();

渲染線程(消耗高)

 

class FRenderingThread : public FRunnable

線程函數:RenderingThreadMain

 All

線程名:RenderThread 1

 

-norenderthread參數可在啟動時強制不開啟RenderThread

注:開啟-norenderthread參數時,最好是帶上-NoLoadingScreen來禁掉loadingscreen,要不然會出現崩潰

 

可用ToggleRenderingThread控制台命令來創建和銷毀RenderThread

銷毀RenderThread時,渲染相關的邏輯會放回到游戲線程中

 

渲染線程的創建邏輯在void StartRenderingThread()函數中

銷毀邏輯在void StopRenderingThread()函數中

 

線程id:uint32 GRenderThreadId

FRunnableThread* GRenderingThread;

會被加入到TaskGraph系統中:FTaskGraphInterface::Get().AttachToThread(RenderThread)

 

bool GUseThreadedRendering; // 是否使用獨立Render線程來渲染

bool GIsThreadedRendering;  // 渲染是否在獨立的Render線程中運行

TAtomic<int32> GIsRenderingThreadSuspended;  // 渲染線程是否暫停

TAtomic<int32> GSuspendRenderingTickables;  // rendering tickables是否應該更新   flush時應暫停更新

 

bool IsInActualRenderingThread();  // 渲染線程存在,且當前線程為渲染線程

bool IsInRenderingThread();  // 無渲染線程||渲染線程暫停||當前線程為渲染線程

bool IsInParallelRenderingThread(); // 與IsInRenderingThread()函數等價

RHI線程

 

class FRHIThread : public FRunnable

 All

使用獨立的渲染線程時,會根據情況來決定是否創建該線程

當渲染線程銷毀時,該線程也會銷毀,RHI執行的邏輯會放回游戲線程中

 

-rhithread參數時(缺省),由各個平台來決定啟動時是否開啟RHI線程

對應bool GRHISupportsRHIThread變量,具體情況如下:

DX11缺省不開啟,可以通過#define EXPERIMENTAL_D3D11_RHITHREAD 1來缺省開啟

DX12在非Editor模式下默認開啟

OpenGL根據FeatureLevel和r.OpenGL.AllowRHIThread的值來決定是否缺省開啟
r.OpenGL.AllowRHIThread 0 // 不使用RHI線程
r.OpenGL.AllowRHIThread 1 // 使用RHI線程(缺省)

Vulklan缺省開啟1個RHI線程
r.Vulkan.RHIThread 0 // 不使用RHI線程
r.Vulkan.RHIThread 1 // 使用1個RHI線程(缺省)
r.Vulkan.RHIThread 2 // 使用多個RHI線程

Metal會根據顯卡芯片版本和r.Metal.IOSRHIThread的值來決定是否缺省開啟
r.Metal.IOSRHIThread 0 // 不使用RHI線程(缺省)
r.Metal.IOSRHIThread 1 // 使用1個RHI線程

 

IOS缺省是沒有開該線程的

 

-norhithread參數會在啟動時強制不開啟rhi線程

如果不開啟rhi線程,rhi的邏輯會跑在RenderingThread線程中

在pc環境的運行時,可用r.RHIThread.Enable 0控制台命令切到這種方式來跑

 

線程id:uint32 GRHIThreadId

FRunnableThread* GRHIThread_InternalUseOnly

會被加入到TaskGraph系統中:FTaskGraphInterface::Get().AttachToThread(ENamedThreads::RHIThread)

 

// 創建獨立的RHIThread,放加入到TaskGraph中,RHI會跑在TaskGraph的RHIThread上

// 在pc環境的運行時,可用r.RHIThread.Enable 1控制台命令切到這種方式來跑

bool GUseRHIThread_InternalUseOnly;  

// 在pc環境的運行時,可用r.RHIThread.Enable 2控制台命令切到這種方式來跑

bool GUseRHITaskThreads_InternalUseOnly;  // TaskGraph中使用Any Thread來跑

 

// GUseRHIThread_InternalUseOnly為true,且正在運行時,該值為true

bool GIsRunningRHIInDedicatedThread_InternalUseOnly;  --》bool IsRunningRHIInDedicatedThread()

// GUseRHITaskThreads_InternalUseOnly為true,且正在運行時,該值為true

bool GIsRunningRHIInTaskThread_InternalUseOnly; --》bool IsRunningRHIInTaskThread()

 

// GUseRHIThread_InternalUseOnly或GUseRHITaskThreads_InternalUseOnly為true,且正在運行時,該值為true

bool GIsRunningRHIInSeparateThread_InternalUseOnly; --》bool IsRunningRHIInSeparateThread()

 

bool IsInRHIThread();

bool IsRHIThreadRunning();  // 存在RHI線程

RenderingThread心跳監視線程

 

class FRenderingThreadTickHeartbeat : public FRunnable

All

線程名:RTHeartBeat 1

 

負責執行rendering thread tickables

 

使用獨立的渲染線程時,才會創建該線程,來執行void TickRenderingTickables()

當渲染線程銷毀時,該線程也會銷毀,TickRenderingTickables會放回游戲線程中來執行

 

TAtomic<bool> GRunRenderingThreadHeartbeat  // 是否使用RenderingThread心跳監視線程

                                                                       // 當使用獨立線程來渲染時,會被設置成true;獨立渲染線程停止時,會被設置成false

float GRenderingThreadMaxIdleTickFrequency = 40.f;  // tick頻率  值越大tick次數越多,性能越差

線程池線程

 

class FQueuedThread : public FRunnable

All

線程名:PoolThread 0  ...  PoolThread <n>

 

// Global thread pool for shared async operations
extern CORE_API FQueuedThreadPool* GThreadPool;
extern CORE_API FQueuedThreadPool* GIOThreadPool;
extern CORE_API FQueuedThreadPool* GBackgroundPriorityThreadPool;

#if WITH_EDITOR
extern CORE_API FQueuedThreadPool* GLargeThreadPool;
#endif

TaskGraph線程(消耗高)

 

class FTaskThreadAnyThread : public FTaskThreadBase

注:class FTaskThreadBase : public FRunnable, FSingleThreadRunnable

 All

線程名:TaskGraphThreadHP 0  ...  TaskGraphThreadHP <n>

           TaskGraphThreadNP 0  ...  TaskGraphThreadNP <n>

           TaskGraphThreadBP 0  ...  TaskGraphThreadBP <n>

進程優先級:HP > NP > BP

 

TaskGraph只在最開始構造函數中創建所有的線程,調用棧如下:

UE4Editor-Core-Win64-Debug.dll!FTaskGraphImplementation::FTaskGraphImplementation(int __formal) Line 1158
UE4Editor-Core-Win64-Debug.dll!FTaskGraphInterface::Startup(int NumThreads) Line 1692
UE4Editor-Win64-Debug.exe!FEngineLoop::PreInitPreStartupScreen(const wchar_t * CmdLine) Line 2052
UE4Editor-Win64-Debug.exe!FEngineLoop::PreInit(const wchar_t * CmdLine) Line 3606
UE4Editor-Win64-Debug.exe!EnginePreInit(const wchar_t * CmdLine)
UE4Editor-Win64-Debug.exe!GuardedMain(const wchar_t * CmdLine)
UE4Editor-Win64-Debug.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 257

AudioThread線程(消耗較高)

 

class FAudioThread : public FRunnable

線程函數:AudioThreadMain

 All

線程名:AudioThread

 

會被加入到TaskGraph系統中:FTaskGraphInterface::Get().AttachToThread(ENamedThreads::AudioThread)

 

bool IsInAudioThread();

AudioMixerRenderThread線程(消耗高)

class IAudioMixerPlatformInterface : public FRunnable, public FSingleThreadRunnable, public IAudioMixerDeviceChangedLister

 All 線程名:AudioMixerRenderThread

class FAsyncLoadingThread final : public FRunnable, public IAsyncPackageLoader

 All

在EDL(EventDrivenLoader)模式下,是不開啟該獨立線程的,在主線程中Loading

bool GEventDrivenLoaderEnabled為ture時為EDL模式

 

編輯器、standalone非cook版本為EDL模式,沒有該線程

Android、IOS等cook版本為Async模式,會開啟該線程

 

bool IsInAsyncLoadingThread(); --》bool IsInAsyncLoadingThreadCoreUObjectInternal()

bool IsAsyncLoading(); --》bool IsAsyncLoadingCoreUObjectInternal()

void SuspendAsyncLoading(); --》void SuspendAsyncLoadingInternal()

void ResumeAsyncLoading(); --》void ResumeAsyncLoadingInternal()

bool IsAsyncLoadingSuspended(); --》bool IsAsyncLoadingSuspendedInternal()

bool IsAsyncLoadingMultithreaded(); --》bool IsAsyncLoadingMultithreadedCoreUObjectInternal()

class FAsyncLoadingThread2 final : public FRunnable, public IAsyncPackageLoader   暫時沒用
class FAsyncLoadingThreadWorker : private FRunnable   暫時沒用

防屏保線程

class FScreenSaverInhibitor : public FRunnable

桌面平台

宏PLATFORM_DESKTOP為1

Windows、Linux、Mac

線程名:ScreenSaverInhibitor

StatsThread線程(消耗高)

class FStatsThread : public FRunnable, FSingleThreadRunnable

 All

線程名:StatsThread

 

會被加入到TaskGraph系統中:FTaskGraphInterface::Get().AttachToThread(ENamedThreads::StatsThread)

FMediaTicker線程

class FMediaTicker : public FRunnable , public IMediaTicker

 All 線程名:FMediaTicker

class FAsyncPurge : public FRunnable

 All

extern int32 GMultithreadedDestructionEnabled;

 

[/Script/Engine.GarbageCollectionSettings]

gc.MultithreadedDestructionEnabled=True // 通過這個控制台命令來開啟

 

多線程GC清理

 


class FSlateLoadingThreadTask : public FRunnable

 All

線程名:SlateLoadingThread1

 

LoadingScreen播放視頻或Slate UI

 

bool IsInSlateThread();

class  FPreLoadScreenSlateThreadTask : public FRunnable  All

線程名:SlateLoadingThread1

 

引擎初始化播放視頻

 

bool IsInSlateThread();

template<typename ResultType>
class TAsyncRunnable : public FRunnable

 All TAsync 0
class FAsyncWriter : public FRunnable, public FArchive  All

FAsyncWriter_UAGame

 

見:OutputDeviceFile.cpp的FAsyncWriter::Run()

異步寫log到文件

class FOnlineAsyncTaskManager : public FRunnable, FSingleThreadRunnable  All OnlineAsyncTaskThreadNull DefaultInstance(1)
class FLwsWebSocketsManager: public IWebSocketsManager, public FRunnable, public FSingleThreadRunnable  All LibwebsocketsThread
class FHttpThread : FRunnable, FSingleThreadRunnable  All HttpManagerThread
class FMessageRouter : public FRunnable, private FSingleThreadRunnable  All FMessageBus.DefaultBus.Router
class FLiveLinkMessageBusDiscoveryManager : FRunnable  All LiveLinkMessageBusDiscoveryManager
class FFileTransferRunnable : public FRunnable  All FFileTransferRunnable
class TcpConsoleListener : FRunnable iOS  
class FTcpListener : public FRunnable iOS  

@implementation FIOSFramePacer

-(void)run:(id)param

iOS  
windows平台Splash線程 windows 線程函數:StartSplashScreenThread
TraceLog windows

windows:線程名為TraceLog;WindowsTrace.cpp下ThreadCreate函數

Android:線程名為bundle id;AndroidTrace.cpp下ThreadCreate函數

IOS:線程名為Thread <n>;AppleTrace.cpp下ThreadCreate函數

windows平台崩潰監視線程 windows 線程函數:CrashReportingThreadProc

Shader編譯線程

class FShaderCompileThreadRunnableBase : public FRunnable

編輯器

線程名:ShaderCompilingThread

拉起ShaderCompileWorker.exe進程進行shader編譯

DistanceField構建線程

class FBuildDistanceFieldThreadRunnable : public FRunnable

編輯器  

class FAssetDataDiscovery : public FRunnable

編輯器

用於發現文件

線程名:FAssetDataDiscovery

 

class FAssetDataGatherer : public FRunnable

編輯器

從FAssetRegistry文件列表中搜集Asset數據

線程名:FAssetDataGatherer

class FVirtualTextureDCCCacheCleanup final : public FRunnable

編輯器 線程名:FVirtualTextureDCCCacheCleanup
class FDDCCleanup : public FRunnable 編輯器 線程名:FDDCCleanup

Wwise編輯器連接線程

class FAkWaapiClientConnectionHandler : public FRunnable

編輯器或windows、mac下的非shipping版本

線程名:WAAPIClientConnectionThread1

 

在Android局內時抓的一個ue4stats文件中,里面統計的線程如下:

 

參考

【UE4源代碼觀察】觀察UE4的線程使用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM