当前位置:网站首页>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
边栏推荐
- Do not use memset to clear floating-point numbers
- 什么是数据泄露
- 【数字IC验证快速入门】20、SystemVerilog学习之基本语法7(覆盖率驱动...内含实践练习)
- How to understand that binary complement represents negative numbers
- [quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)
- What are PV and UV? pv、uv
- 大表delete删数据导致数据库异常解决
- unnamed prototyped parameters not allowed when body is present
- Whether runnable can be interrupted
- The difference between full-time graduate students and part-time graduate students!
猜你喜欢

写一篇万字长文《CAS自旋锁》送杰伦的新专辑登顶热榜

2022全开源企业发卡网修复短网址等BUG_2022企业级多商户发卡平台源码
![[deep learning] image hyperspectral experiment: srcnn/fsrcnn](/img/84/114fc8f0875b82cc824e6400bcb06f.png)
[deep learning] image hyperspectral experiment: srcnn/fsrcnn

Zhongang Mining: Fluorite continues to lead the growth of new energy market

Getting started with webgl (1)

【数字IC验证快速入门】23、SystemVerilog项目实践之AHB-SRAMC(3)(AHB协议基本要点)
![Super signature principle (fully automated super signature) [Yun Xiaoduo]](/img/b8/5bafbada054b335568e64c7e1ac6bb.jpg)
Super signature principle (fully automated super signature) [Yun Xiaoduo]

Bye, Dachang! I'm going to the factory today

Unity's ASE realizes cartoon flame

全日制研究生和非全日制研究生的区别!
随机推荐
A need to review all the knowledge, H5 form is blocked by the keyboard, event agent, event delegation
Shader Language
How to create Apple Developer personal account P8 certificate
Qu'est - ce qu'une violation de données
Cut ffmpeg as needed, and use emscripten to compile and run
[target detection] yolov5 Runtong voc2007 data set
[original] all management without assessment is nonsense!
【Markdown语法高级】让你的博客更精彩(四:设置字体样式以及颜色对照表)
Oracle control file loss recovery archive mode method
【OBS】RTMPSockBuf_ Fill, remote host closed connection.
Connecting FTP server tutorial
XMIND frame drawing tool
银行需要搭建智能客服模块的中台能力,驱动全场景智能客服务升级
[follow Jiangke University STM32] stm32f103c8t6_ PWM controlled DC motor_ code
Implementation of crawling web pages and saving them to MySQL using the scrapy framework
一大波开源小抄来袭
Getting started with webgl (2)
有钱人买房就是不一样
Basic knowledge sorting of mongodb database
Zhongang Mining: Fluorite continues to lead the growth of new energy market