当前位置:网站首页>Ue4/ue5 multi thread development attachment plug-in download address
Ue4/ue5 multi thread development attachment plug-in download address
2022-07-07 15:42:00 【WhiteTian】
Original article , Reprint please indicate the source .
Unreal Engine Introduction to multithreading development
- Preface
- Download address of encapsulated plug-ins :
- UE4 There's a cable pool , Why should I encapsulate a thread pool ?
- 1>FRunnable
- 2>TGraphTask
- 3>FAsyncTask
- 4. My plugins
- 4.1 Create a new thread pool -> Synchronous and asynchronous invocation
- 4.2 encapsulation UE4 Provided thread pool -> Synchronous and asynchronous invocation
- 4.3 EventGraph Use macros
- 4.4 Resources are loaded asynchronously Not tested yet , Not very easy to use , Still improving , In the process of continuous improvement of plug-ins
- 5. Several precautions
- 6. Thinking that needs to be done
Preface
I'm doing some resources these days Load.
During the test phase, the data loading process has been stuck for several minutes , So I want to use multithreading .
Mainly considering the reusability and simplicity of use . Don't write a lot of code , So it took four days to make a multi-threaded encapsulated plug-in , I didn't go back to Hebei at the weekend , It's all assigned to this plug-in .
Functions include :
1> Encapsulates the FRunnable, We made our own thread pool .
2> Encapsulates the FTaskGraph, Made management classes and macro encapsulation .
3> Also encapsulated FNonAbandonableTask, Also is to use FAsyncTask To use UE4 Thread pools do things .
4> It encapsulates FStreamableManager.
Download address of encapsulated plug-ins :
2021-09-29 14:28:19 The plug-in has been updated
Improvements 1: Improved thread pool .
Improvements 2: Add a method to the thread pool . The specific logic is Clear the unexecuted tasks in the queue in the thread pool , Block main thread , And wait for the executing thread logic , Wait for the main thread to open after execution . call StopThreadLogic() You can achieve .
The plug-in has been uploaded again ,2021-10-29 11:20:08.
Download address
The plug-in is introduced below The fourth quarter,
Resources are loaded asynchronously Not tested yet , Not very easy to use , Still improving , In the process of continuous improvement of plug-ins
UE4 There's a cable pool , Why should I encapsulate a thread pool ?
I think so ,UE4 Of AsyncTask There is also a thread pool inside + The concept of queues ,FQueuedThreadPool.
One is Engine In fact, there are many places to call its built-in thread pool , Of course, it's not exclusion , We can use it, too ;
The main consideration is the second point :UE4 It also provides a way to create a new thread , That is to say FRunnable, If we call FRunnable To create a new thread , Just use , After that ? The direct thread exited , The thread is then cleaned up . Creating and releasing threads is costly .
About the consumption of creating threads : Some people have also done some tests
Since there is consumption , There are also efficient reuse schemes , Thread pool . This is why I want to encapsulate a thread pool .
Say a bit first UE4 The basic knowledge of several threads is as follows :
1>FRunnable
1> Create a new independent thread .
2> It is usually written as a base class , Inherit once . Write our content on the new class .
3> The order in which it is called Init(), Run(), Exit().
4> If initialization fails , The thread will stop executing and return an error code .
5> If initialization is successful ,Run() Inside the function will be where we write logic .
6> Exit the thread by calling Exit() To clean up .
In fact, if we don't manage this thread , stay Run() Yes return 0 Then it will go to the thread Exit() Part of , The thread exits and is cleaned up .
Non encapsulated code , Yes FRunnable Use , Just use , It's not good-looking (cpp I also added a thread and thread switching , In fact, that is what we will introduce below ), But I believe it can also be a reference :
The header file
// Fill out your copyright notice in the Description page of Project Settings.
//tianhuajian/whitetian made in 2021-06-30 10:41:24
//UE4 Threads 1: Create a new independent thread
#pragma once
#include "CoreMinimal.h"
#include "HAL/Runnable.h"
DECLARE_DELEGATE(FMySingleRunnableDelegate);
class FMySingleRunnable : public FRunnable
{
public:
FMySingleRunnable();
// Thread initialization , Put your initialization code If return false Then it won't be executed Run
virtual bool Init();
// Execute your business logic
virtual uint32 Run();
// Thread pause
virtual void Stop();
// Thread to exit
virtual void Exit();
// Create my thread
void MyCreate();
// My independent thread object
FRunnableThread* pThread;
FMySingleRunnableDelegate m_del;
};
cpp file
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyRunnableTest.h"
#include "GameWorldBaseGameModeBase.h"
FMySingleRunnable::FMySingleRunnable()
: pThread(nullptr)
{
}
bool FMySingleRunnable::Init()
{
UUtilsLibrary::Log(TEXT("tianhuajian_standlone_thread Init"), true);
return true;
}
uint32 FMySingleRunnable::Run()
{
// Write your business logic in this thread , Test code
UUtilsLibrary::Log(TEXT("tianhuajian_standlone_thread Run 1"), true);
// Switch between threads
FGraphEventRef Task = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
UUtilsLibrary::Log(TEXT("change thread"), true);
}, TStatId(), nullptr, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(Task);
UUtilsLibrary::Log(TEXT("tianhuajian_standlone_thread Run 2"), true);
return 0;
}
void FMySingleRunnable::Stop()
{
UUtilsLibrary::Log(TEXT("tianhuajian_standlone_thread Stop"), true);
}
void FMySingleRunnable::Exit()
{
//UUtilsLibrary::Log(TEXT("tianhuajian_standlone_thread Exit"), true);
}
void FMySingleRunnable::MyCreate()
{
pThread = FRunnableThread::Create(this, TEXT("tianhuajian_standlone_thread"), 0, TPri_Normal);
}
The test case
class FMySingleRunnable* pThrad;
// Test independent threads
pThrad = new FMySingleRunnable();
pThrad->m_del.BindUObject(this, &AGameWorldBaseGameModeBase::PrintF);
pThrad->MyCreate();
stay DoWork Where the breakpoint , See if it's your thread .
Here is the basic usage of icon thread
2>TGraphTask
This is an asynchronous processing scheme ,
One is to allow you to specify the one you want to execute your Gameplay The thread of ,
The second is to allow you to specify the order of task execution .
About the characteristics of task execution sequence : It supports the sequence of tasks , It can execute a TGraphTask, Save it
References to it, such as A Well , At this time, you want to finish the previous A The next task is to be performed after the task TGraphTask Mission B. Then this system can help .
I searched the engine TGraphTask There are many places to use , Familiar to us Tick In fact, it is through our icon task that we finally execute
I'll just find one Actor Or component Tick Segment points , Look at the screenshot
Of course, you can leave it in order , Just write enumeration in the template method
Need to rely on you is defined as FireAndForget, Without dependency, it is defined as TrackSubsequents.
static ESubsequentsMode::Type GetSubsequentsMode() {
return ESubsequentsMode::TrackSubsequents; }
namespace ESubsequentsMode
{
enum Type
{
/** It is necessary when another task will depend on this task . */
TrackSubsequents,
/** It can be used to save the cost of task map , The mission launched will not be dependent on another mission . */
FireAndForget
};
}
I may not know what it is
Let me list the basic usage of this , My test code , It is also not encapsulated , You can refer to
The header file
// Fill out your copyright notice in the Description page of Project Settings.
//tianhuajian/whitetian made in 2021-06-30 10:41:24
//UE4 Threads 2: FGraphTask, Use idle threads
#pragma once
#include "CoreMinimal.h"
#include "GameWorldBaseGameModeBase.h"
class FMyGraphTaskTest
{
public:
FMyGraphTaskTest();
ENamedThreads::Type TargetThread;
TFunction<void()> TheTask;
FMyGraphTaskTest(ENamedThreads::Type Thread, TFunction<void()>&& Task) : TargetThread(Thread), TheTask(MoveTemp(Task)) {
}
void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
{
TheTask();
// Where logic is written
UUtilsLibrary::Log(TEXT("SADSADSA"), true);
}
static ESubsequentsMode::Type GetSubsequentsMode() {
return ESubsequentsMode::TrackSubsequents; }
// Who idle who
ENamedThreads::Type GetDesiredThread() {
return (TargetThread)/*ENamedThreads::AnyThread*/; }
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FMyGraphTaskTest, STATGROUP_TaskGraphTasks);
}
};
CPP file
There is nothing in the structure , In fact, you can pass some parameters into it
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyGraphTaskTest.h"
FMyGraphTaskTest::FMyGraphTaskTest()
{
}
The test case
// Specify any thread to execute our logic
int32 a = 112134;
TGraphTask<FMyGraphTaskTest>::CreateTask(NULL, ENamedThreads::AnyThread).ConstructAndDispatchWhenReady(ENamedThreads::AnyThread, [a]()
{
UUtilsLibrary::Log(FString::FromInt(a), true);
});
Does he have any shortcomings ?
A personal opinion , It's also what I found in my test , For example, I specify a thread ( such as AnyThread) To carry out my GamePlay, But at this time, maybe he will point me out
MainThread, That is, the main thread / Game threads . If my execution is a complex logic , At this time, it is just allocated to the main thread for execution , At this time, the main thread will be blocked .
I don't know why I didn't design one called IdelThread, Idle thread enumeration ( image AnyThread/GameThread Definition of enumeration );
Therefore, it is recommended not to specify AnyThread It's time to implement too complicated logic , Because it may block your game thread .
3>FAsyncTask
This system is actually UE4 Provided thread pool . Inherited from FNonAbandonableTask. It can support us to perform complex calculations in parallel .
Its implementation principle is in the engine Init According to CPU Kernel number, etc ( I haven't corrected it in detail according to what standards ) Create a thread pool .
There is also a queue concept in this thread pool Queue.
analysis 1: When the threads in the thread pool finish executing , Just hang it up , Mark as idle ;
analysis 2: The other is the running state ;
situation 1: At this time, when logic comes in , It will judge whether there are idle threads in the thread pool , If there is , Give logic to this idle thread ,
And wake it up , Execute our logic ;
situation 2: At this time, when logic comes in , Threads are busy , Add your logic to queue Inside , Always judge , Is there any idle thread ?
Has it ? If so, take out the last one in my queue , Put it on the idle thread to execute , And delete this logic in the queue .
My thread pool is also based on ideas .
How to use it
The header file
#pragma once
#include "CoreMinimal.h"
class TaskAsyncTask : public FNonAbandonableTask
{
friend class FAsyncTask<TaskAsyncTask>;
int32 InstanceInt;
TaskAsyncTask( int32 _InstanceInt)
:InstanceInt(_InstanceInt)
{
}
void DoWork()
{
UE_LOG(LogTemp, Log, TEXT("DoWork %d"), InstanceInt);
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(TaskAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
}
};
CPP file , No doubt. ,cpp There is nothing in it , You can also not write it
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyAsyncTaskTest.h"
The test case
StartBackgroundTask() Asynchronous operations
StartSynchronousTask() Synchronous operation
FAsyncTask<TaskAsyncTask> *MyTask = new FAsyncTask<TaskAsyncTask>(3);
// MyTask->StartBackgroundTask(); // asynchronous The current thread will not block
MyTask->StartSynchronousTask();// Sync , The current thread will block
if (MyTask->IsDone())
{
UE_LOG(LogTemp, Log, TEXT("MyTask->IsDone()"));
}
MyTask->EnsureCompletion();
delete MyTask;
The test case 2, UE The shortcut provided
AsyncTask(ENamedThreads::AnyThread, [&]() {
UUtilsLibrary::Log("test ue function");
});
The test case 3, UE The shortcut provided
This way is not simple . It's easier to encapsulate a macro and bind a proxy . So I did this work in the plug-in .
// Asynchronous execution
(new FAutoDeleteAsyncTask<TaskAsyncTask>(Delegate))->StartBackgroundTask();
// Synchronous execution
(new FAutoDeleteAsyncTask<TaskAsyncTask>(Delegate))->StartSynchronousTask();
4. My plugins
Use the thread pool I wrote , Execute one Raw、Lambda、WeakLambda、Static、UFunction、UObject、SP、ThreadSafeSP
Raw:C++ A native method
SP:UE4 Intelligent pointer fast Classes of patterns
ThreadSafeSP:UE4 Intelligent pointer threadsafe Classes of patterns
This is lambda Example
void ARuntimeActor::AsyncInit(FModelMesh* mesh)
{
auto lambda = [&, mesh]() {
Init(mesh);
};
GEKThread::GetPoolTask().CreateAsyncLambda(lambda);
}
4.1 Create a new thread pool -> Synchronous and asynchronous invocation
// Create a new thread pool -> Asynchronous methods
GEKThread::GetPoolTask().CreateAsyncRaw(***);
GEKThread::GetPoolTask().CreateAsyncLambda(***);
GEKThread::GetPoolTask().CreateAsyncWeakLambda(***);
GEKThread::GetPoolTask().CreateAsyncStatic(***);
GEKThread::GetPoolTask().CreateAsyncUFunction(***);
GEKThread::GetPoolTask().CreateAsyncUObject(***);
GEKThread::GetPoolTask().CreateAsyncSP(***);
GEKThread::GetPoolTask().CreateAsyncThreadSafeSP(***);
// Create a new thread pool -> Asynchronous methods
GEKThread::GetPoolTask().CreateSyncRaw(***);
GEKThread::GetPoolTask().CreateSyncLambda(***);
GEKThread::GetPoolTask().CreateSyncWeakLambda(***);
GEKThread::GetPoolTask().CreateSyncStatic(***);
GEKThread::GetPoolTask().CreateSyncUFunction(***);
GEKThread::GetPoolTask().CreateSyncUObject(***);
GEKThread::GetPoolTask().CreateSyncSP(***);
GEKThread::GetPoolTask().CreateSyncThreadSafeSP(***);
4.2 encapsulation UE4 Provided thread pool -> Synchronous and asynchronous invocation
// encapsulation UE4 Provided thread pool -> Asynchronous methods
GEKThread::GetAsyncTask().CreateAsyncRaw(***);
GEKThread::GetAsyncTask().CreateAsyncLambda(***);
GEKThread::GetAsyncTask().CreateAsyncWeakLambda(***);
GEKThread::GetAsyncTask().CreateAsyncStatic(***);
GEKThread::GetAsyncTask().CreateAsyncUFunction(***);
GEKThread::GetAsyncTask().CreateAsyncUObject(***);
GEKThread::GetAsyncTask().CreateAsyncSP(***);
GEKThread::GetAsyncTask().CreateAsyncThreadSafeSP(***);
// encapsulation UE4 Provided thread pool -> Asynchronous methods
GEKThread::GetAsyncTask().CreateSyncRaw(***);
GEKThread::GetAsyncTask().CreateSyncLambda(***);
GEKThread::GetAsyncTask().CreateSyncWeakLambda(***);
GEKThread::GetAsyncTask().CreateSyncStatic(***);
GEKThread::GetAsyncTask().CreateSyncUFunction(***);
GEKThread::GetAsyncTask().CreateSyncUObject(***);
GEKThread::GetAsyncTask().CreateSyncSP(***);
GEKThread::GetAsyncTask().CreateSyncThreadSafeSP(***);
4.3 EventGraph Use macros
You can execute your logic on the thread you specify , And you can pass a message that you have to wait for GraphEvent Thread logic , order of appointment .
//FGraphTask Use definition : Please refer to EKThreadGraphManager.h Header notes for
#pragma region Macro_FEKGraphTask_MacroDefine
// Switch to the specified thread (CallThreadName) Execute our agent on (PS: Do not call this method , This method mainly provides for the following bindings . Directly call the following ) Support task waiting
#define EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, InTaskDeletegate) \ FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(InTaskDeletegate, TStatId(), WaitOtherGraphTask, CallThreadName);
//3.1 Switch to the specified thread (CallThreadName) Execute our Raw agent
#define EK_CALL_THREAD_RAW(WaitOtherGraphTask, CallThreadName, Object, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateRaw(Object, __VA_ARGS__))
//3.2 Switch to the specified thread (CallThreadName) Execute our Lambda
#define EK_CALL_THREAD_LAMBDA(WaitOtherGraphTask, CallThreadName, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateLambda(__VA_ARGS__))
//3.3 Switch to the specified thread (CallThreadName) Execute our WeakLambda
#define EK_CALL_THREAD_WEAKLAMBDA(WaitOtherGraphTask, CallThreadName, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateWeakLambda(__VA_ARGS__))
//3.4 Switch to the specified thread (CallThreadName) Execute our Static
#define EK_CALL_THREAD_STATIC(WaitOtherGraphTask, CallThreadName, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateStatic(__VA_ARGS__))
//3.5 Switch to the specified thread (CallThreadName) Execute our UFunction agent
#define EK_CALL_THREAD_UFUNCTION(WaitOtherGraphTask, CallThreadName, Object, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateUFunction(Object, __VA_ARGS__))
//3.6 Switch to the specified thread (CallThreadName) Execute our UObject agent
#define EK_CALL_THREAD_UOBJECT(WaitOtherGraphTask, CallThreadName, Object, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateUObject(Object, __VA_ARGS__))
//3.7 Switch to the specified thread (CallThreadName) Execute our SPFast agent
#define EK_CALL_THREAD_SP(WaitOtherGraphTask, CallThreadName, Object, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateSP(Object, __VA_ARGS__))
//3.8 Switch to the specified thread (CallThreadName) Execute our SPSafe agent
#define EK_CALL_THREAD_SPSAFE(WaitOtherGraphTask, CallThreadName, Object, ...) \ EK_CALL_THREAD(WaitOtherGraphTask, CallThreadName, FSimpleDelegate::CreateThreadSafeSP(Object, __VA_ARGS__))
// Wait for a task ,UE4 The name is reversed . Note that this will block .
#define EK_WAITING_OTHER_THREAD_SINGLE_COMPLETED(EventRef) FTaskGraphInterface::Get().WaitUntilTaskCompletes(EventRef)
// Wait for an array task ,UE4 The name is reversed . Note that this will block .
#define EK_WAITING_OTHER_THREADS_ARRAY_COMPLETED(EventRef) FTaskGraphInterface::Get().WaitUntilTasksComplete(EventRef)
#pragma endregion
4.4 Resources are loaded asynchronously Not tested yet , Not very easy to use , Still improving , In the process of continuous improvement of plug-ins
GEKThread::GetStreamble().CreateAsyncRaw(***);
GEKThread::GetStreamble().CreateAsyncLambda(***);
GEKThread::GetStreamble().CreateAsyncWeakLambda(***);
GEKThread::GetStreamble().CreateAsyncStatic(***);
GEKThread::GetStreamble().CreateAsyncUFunction(***);
GEKThread::GetStreamble().CreateAsyncUObject(***);
GEKThread::GetStreamble().CreateAsyncSP(***);
GEKThread::GetStreamble().CreateAsyncThreadSafeSP(***);
5. Several precautions
Consider the following code , I want to finish the creation in the main thread UI The logic of , And wait for him to finish . Can you see the hidden dangers of the following logic ?
The following logic is not GameThread There is no problem with the thread execution of , But if this method is executed in the main thread , Wait for the main thread to finish executing , At this time, the main thread is stuck .
// Check the progress bar UI Legitimacy
void USPGameInstance::CheckProgressUI()
{
if (!IsValid(m_pProgress))
{
// If it is UI If you need to create , Go to the main thread to create it . And you can't continue until you create it .
auto CreateUIFunc = EK_CALL_THREAD_LAMBDA(nullptr, ENamedThreads::GameThread, [&]()
{
// Create a global progress bar
auto widget = GGameInstance->UIManager()->OpenUI(UI_DoubleMode_Progress);
m_pProgress = Cast<USPProgress>(widget);
});
EK_WAITING_OTHER_THREAD_SINGLE_COMPLETED(CreateUIFunc);
}
}
So we need to pay attention to :
1> No longer your thread executing logic , Wait for the current thread ; For example, it is already in the main thread , Wait for the main thread again ; wrong !
2> The following introduction , There's a lot on the Internet
There should be many introductions on the Internet , Don't be in Africa GameThread Do the following . In fact, this is what I summarized in another blog , It has become private for the time being .
6. Thinking that needs to be done
Thread creation takes up stack space , No matter how sweet the sugar is, you can't eat it all the time , You can't create without restrictions .
thank you , It's not easy to create , Great Xia, please stay … Move your lovely hands , Give me a compliment before you go <( ̄︶ ̄)>
ღ( ´・ᴗ・` ) finger heart
边栏推荐
- unnamed prototyped parameters not allowed when body is present
- 使用Scrapy框架爬取网页并保存到Mysql的实现
- 【数字IC验证快速入门】25、SystemVerilog项目实践之AHB-SRAMC(5)(AHB 重点回顾,要点提炼)
- 【目标检测】YOLOv5跑通VOC2007数据集
- Runnable是否可以中断
- 【深度学习】语义分割实验:Unet网络/MSRC2数据集
- [quick start of Digital IC Verification] 25. AHB sramc of SystemVerilog project practice (5) (AHB key review, key points refining)
- Syntax of generator function (state machine)
- 知否|两大风控最重要指标与客群好坏的关系分析
- [quickstart to Digital IC Validation] 20. Basic syntax for system verilog Learning 7 (Coverage Driven... Including practical exercises)
猜你喜欢
Mathematical modeling -- what is mathematical modeling
【兰州大学】考研初试复试资料分享
Qu'est - ce qu'une violation de données
Write sequence frame animation with shader
Vertex shader to slice shader procedure, varying variable
The rebound problem of using Scrollview in cocos Creator
[quick start of Digital IC Verification] 29. Ahb-sramc (9) (ahb-sramc svtb overview) of SystemVerilog project practice
Tkinter after how to refresh data and cancel refreshing
[follow Jiangke University STM32] stm32f103c8t6_ PWM controlled DC motor_ code
Wechat applet 01
随机推荐
从 1.5 开始搭建一个微服务框架链路追踪 traceId
Syntax of generator function (state machine)
【数字IC验证快速入门】24、SystemVerilog项目实践之AHB-SRAMC(4)(AHB继续深入)
微信小程序 01
XMIND frame drawing tool
大表delete删数据导致数据库异常解决
Nacos conformance protocol cp/ap/jraft/distro protocol
Annexb and avcc are two methods of data segmentation in decoding
The significance of XOR in embedded C language
[target detection] yolov5 Runtong voc2007 data set
How to release NFT in batches in opensea (rinkeby test network)
The download button and debug button in keil are grayed out
A need to review all the knowledge, H5 form is blocked by the keyboard, event agent, event delegation
MongoD管理数据库的方法介绍
How to deploy the super signature distribution platform system?
Pat grade a 1103 integer factorizatio
摘抄的只言片语
Unity's ASE achieves full screen sand blowing effect
Mathematical modeling -- what is mathematical modeling
HW primary flow monitoring, what should we do