UE4--多线程的实现方式

首先查阅了WIKI中能找到Rama大神的两篇文章,讲了两个开线程的方式:

https://wiki.unrealengine.com/Multi-Threading:_Task_Graph_System
https://wiki.unrealengine.com/Multi-Threading:_How_to_Create_Threads_in_UE4

TaskGraph与FRunnable的比较

一篇是用任务系统实现的多线程,一篇是用的FRunnable和FRunnableThread线程类。前者适用于相对较小的功能实现,或者是需要开多个线程,各自实现一块较小的功能或者计算的情况;而后者才是适用于复杂运算或是较大的功能模块的。

Rama用这两种方法分别实现了一个计算前50000个质数的例子。任务系统中,每个线程只执行一个质数的计算,系统开了多个线程,其实效率是很低的;而采用了FRunnable则只开一个线程来执行整个质数计算过程,效率相对较高。Rama的例子中,前者运行时主游戏画面只有45帧左右,而后者运行时能保持在90帧左右。

而且任务系统有时候会使用我们的游戏线程,因此对于大型的任务应该使用FRunnable

使用多线程的注意事项

当需要进行复杂繁多的计算时,我们需要开线程来计算以防止免游戏主线程卡死。但须注意,在GameThread线程之外的其他线程中,不允许做以下事情:
不要 spawn / modify / destroy UObjects / AActors
不要使用定时器 TimerManager(TimerManager只能在主线程中)
不要使用任何绘制接口,例如 DrawDebugLine/Point,不然有可能崩溃

在本科操作系统的课程中我们就学过,多进程或线程之间的数据交互可能会造成死锁,为此才有了后面的银行家算法等加锁的算法…因此我们新开的线程中不允许做上述事情,一般只是用来实现复杂计算,网络连接等功能…

还有一点要注意,当创建太多线程时,有可能达到CPU的并发上限,这时这些并发线程会为了争夺CPU的执行时间而彼此阻碍,我们可以在FQueuedThreadPool中限制线程数量。

任务系统

从Engine\Source\Runtime\Core\Public\Async\AsyncWork.h开始分析。

AsyncWork.h中提供了两个任务模板类:FAutoDeleteAsyncTask和FAsyncTask,区别是执行结束后是否自动销毁。还为我们写了对应的example,稍后我将讲述FAutoDeleteAsyncTask的example,但是Rama大神的任务系统例子中并没有使用这两个模板类,看的有点懵逼=。=
其中的DoWork函数是任务执行具体内容的接口。引擎注释:
Tells the user job to do the work, sometimes called synchronously, sometimes from the thread pool. Calls the event tracker.
通知用户进程来work,有时是同步的,有时是从线程池中进行。会调用事件追踪器。

AsyncWork.h中有一个不能被abandon的基类,我们自定义的任务类就是继承自她的:

/*** Stub class to use a base class for tasks that cannot be abandoned*/
class FNonAbandonableTask
{
public:bool CanAbandon(){return false;}void Abandon(){}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

根据AsyncWork.h中提供的模板example,使用任务系统的流程:
先自定义一个任务类,用于执行我们的任务:

class ExampleAutoDeleteAsyncTask : public FNonAbandonableTask
{friend class FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>;int32 ExampleData;//构造ExampleAutoDeleteAsyncTask(int32 InExampleData): ExampleData(InExampleData){}//自己定义任务具体执行内容void DoWork(){//... do the work here}FORCEINLINE TStatId GetStatId() const{RETURN_QUICK_DECLARE_CYCLE_STAT(ExampleAutoDeleteAsyncTask, STATGROUP_ThreadPoolAsyncTasks);}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

FAutoDeleteAsyncTask模板类的定义可以在AsyncWork.h中查看,这里就不贴了,使用多线程时,new出来即可:

void Example()
{// start an example job(new FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>(5)->StartBackgroundTask();// do an example job now, on this thread(new FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>(5)->StartSynchronousTask();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

任务系统的多线程执行流程在http://blog.csdn.net/tuanxuan123/article/details/52780629里有具体分析,我们可以看到,其实任务系统最后创建的也是FRunnableThread这个线程类:

FQueuedThread::Create()
{DoWorkEvent = FPlatformProcess::GetSynchEventFromPool();Thread = FRunnableThread::Create(this, *PoolThreadName, InStackSize, ThreadPriority, FPlatformAffinity::GetPoolThreadMask());
}
  • 1
  • 2
  • 3
  • 4
  • 5

FRunnable

FRunnable:是线程使用类
FRunnableThread:是具体的线程类
实际创建新线程时,自定义一个类继承自FRunnable,FRunnable中的Init(),Run(),Stop(),Exit()都是可供我们重写的虚函数。
我们可以在构造函数中使用FRunnableThread::Create函数来创建一个新的线程。我们来看FRunnableThread::Create函数:

FRunnableThread* FRunnableThread::Create(
    class FRunnable* InRunnable, const TCHAR* ThreadName,uint32 InStackSize,EThreadPriority InThreadPri, uint64 InThreadAffinityMask)
{FRunnableThread* NewThread = nullptr;if (FPlatformProcess::SupportsMultithreading()){check(InRunnable);// Create a new thread objectNewThread = FPlatformProcess::CreateRunnableThread();if (NewThread){// Call the thread's create methodif (NewThread->CreateInternal(InRunnable,ThreadName,InStackSize,InThreadPri,InThreadAffinityMask) == false){// We failed to start the thread correctly so clean updelete NewThread;NewThread = nullptr;}}}else if (InRunnable->GetSingleThreadInterface()){// Create a fake thread when multithreading is disabled.NewThread = new FFakeThread();if (NewThread->CreateInternal(InRunnable,ThreadName,InStackSize,InThreadPri) == false){// We failed to start the thread correctly so clean updelete NewThread;NewThread = nullptr;}}#if STATSif( NewThread ){FStartupMessages::Get().AddThreadMetadata( FName( *NewThread->GetThreadName() ), NewThread->GetThreadID() );}
#endif // STATSreturn NewThread;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

if (FPlatformProcess::SupportsMultithreading())先判断平台是否支持多线程:

平台支持多线程:

根据当前的平台会执行对应系统的CreateRunnableThread函数,我们是Window系统下:

FRunnableThread* FWindowsPlatformProcess::CreateRunnableThread()
{return new FRunnableThreadWin();
}
  • 1
  • 2
  • 3
  • 4

创建了一个继承自FRunnableThread的FRunnableThreadWin类,之后在CreateInternal函数中:

virtual bool CreateInternal( FRunnable* InRunnable, const TCHAR* InThreadName,uint32 InStackSize = 0,EThreadPriority InThreadPri = TPri_Normal, uint64 InThreadAffinityMask = 0 ) override{check(InRunnable);Runnable = InRunnable;ThreadAffinityMask = InThreadAffinityMask;// Create a sync event to guarantee the Init() function is called firstThreadInitSyncEvent = FPlatformProcess::GetSynchEventFromPool(true);// Create the new threadThread = CreateThread(NULL,InStackSize,_ThreadProc,this,STACK_SIZE_PARAM_IS_A_RESERVATION,(::DWORD *)&ThreadID);// If it fails, clear all the varsif (Thread == NULL){Runnable = nullptr;}else{FThreadManager::Get().AddThread(ThreadID, this);// Let the thread start up, then set the name for debug purposes.ThreadInitSyncEvent->Wait(INFINITE);ThreadName = InThreadName ? InThreadName : TEXT("Unnamed UE4");SetThreadName( ThreadID, TCHAR_TO_ANSI( *ThreadName ) );SetThreadPriority(InThreadPri);}// Cleanup the sync eventFPlatformProcess::ReturnSynchEventToPool(ThreadInitSyncEvent);ThreadInitSyncEvent = nullptr;return Thread != NULL;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

函数执行了具体的线程创建,并将新创建的线程add到线程管理器FThreadManager中,并设置了相关的一些属性。而我们的新线程在_ThreadProc回调函数已经开始执行。

平台不支持多线程:

UE4会帮我们new一个假线程 NewThread = new FFakeThread();
FFakeThread也是继承自FRunnableThread的线程类,FFakeThread的构造函数就会执行添加到FThreadManager的函数:

    FFakeThread(): bIsSuspended(false), Runnable(nullptr){ThreadID = ThreadIdCounter++;// Auto register with single thread manager.FThreadManager::Get().AddThread(ThreadID, this);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

之后执行期间FThreadManager会判断系统是否支持多线程并执行相应流程。

撸主能力不够,看了几位大神的博客不太明白,好在老大帮我拆了FRunnable流程的代码,因此记录一下。如有错误或不当之处,还望各位大神不吝赐教!

参考:
http://blog.csdn.net/noahzuo/article/details/51372972
http://blog.csdn.net/tuanxuan123/article/details/52780629
https://wiki.unrealengine.com/Multi-Threading:_Task_Graph_System
https://wiki.unrealengine.com/Multi-Threading:_How_to_Create_Threads_in_UE4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432522.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python if else格式_Python if else条件语句详解

我们看到的代码都是顺序执行的&#xff0c;也就是先执行第1条语句&#xff0c;然后是第2条、第3条……一直到最后一条语句&#xff0c;这称为顺序结构。但是对于很多情况&#xff0c;顺序结构的代码是远远不够的&#xff0c;比如一个程序限制了只能成年人使用&#xff0c;儿童因…

UE4 HTC VIVE - 番外篇 - 局域网联机(一)

--------------------引擎环境配置文件修改与项目在线模式启动修改--------------------1&#xff09;我们就直接用默认名创建一个第三人称项目Paste_Image.png2&#xff09;右键资源栏&#xff0c;创建一个新的C类创建一个C类选择不继承任何UE提供的基类3&#xff09;打开【解…

UIImageView 圆角

UIImageView *image [[UIImageView alloc]init]; image.layer.cornerRadius 10.0;//如果想变圆形&#xff0c;设置这个值为宽度的一半 image.layer.masksToBounds YES;转载于:https://www.cnblogs.com/qianyindichang/p/3779823.html

java 参数 string_java(String和StringBuffer分别作为参数传递)

package com.day8.StringBuffer;public class Demo7StringBuffer {/***常见对象(String和StringBuffer分别作为参数传递)* A:形式参数问题* String作为参数传递* StringBuffer作为参数传递* B:案例演示* String和StringBuffer分别作为参数传递问题*( 基本数据类型的值传递&…

UE4 HTC VIVE - 番外篇 - 局域网联机(二)

开始之前先说一下网游中服务器与客户端的大致关系&#xff1a;网络游戏中各段关系图客户端职责&#xff1a;1&#xff09;接收玩家的输入翻译得到【玩家指令】上传服务器&#xff1b;2&#xff09;接收服务器下发的【游戏指令】并将其实现服务器职责&#xff1a;1&#xff09;接…

启动文件、简单的消息框

C中打开文件的方法。 1.system&#xff08;&#xff09;; 函数原型&#xff1a; int system(char *command); 作用&#xff1a;发出一个DOS命令。 特点&#xff1a;该函数是同步的&#xff0c;不灵活。只是能够改为system("start XXX"); 2.WinExec&#xff08;&#…

UE4 HTC VIVE - 番外篇 - 局域网联机三

环境设置和检测1.打开引擎下的在线调试引擎在线模式开启2.打开项目的在线调试项目在线模式开启3.检查机器网段我们需要对每台要加入局域网的机器都进行网段检测&#xff0c;这是能客户端能找到主机广播的前提&#xff0c;否则无法搜到主机Windows键R键输入CMD输入ipconfig检查I…

java异常判断_Java异常退出条件的判断示例代码

无论是功能性代码还是算法性代码&#xff0c;程序都是一系列流程的合集既然是流程就分为&#xff1a;一般流程和异常流程&#xff1b;一般流程保证了基本功能&#xff1b;异常流程则是对程序稳定性的保证&#xff0c;不能因为一些非法输入&#xff0c;项目就挂了&#xff1b;注…

POJ 3225 - 区间

待整理。 转载于:https://www.cnblogs.com/e0e1e/p/poj_3225.html

UE4 HTC VIVE 多人联机

1. editor的VR模式不支持网络&#xff0c;所以在VR模式下没法调试多人联机程序 2. editor的standalone模式&#xff0c;引擎的源码里面把VR模式关闭了&#xff0c;所以需要修改引擎源码 3.可以在命令行下打开VR模式

关于64位Linux配置android开发环境出现 No such file or directory

前几天在64位系统上部署android开发环境的时候出现了这种问题 /aapt: No such file or directory 通过谷老师&#xff0c;知道原理android SDK里面的程序全是32位的&#xff0c;没有64位的。不知道为啥要写个x64&#xff0c; 我们仅仅要安装一下32位的执行库即可了&#xff0c;…

java读取txt文件内容_Java读取TXT文件内容的方法

Java读取txt文件内容。可以作如下理解&#xff1a;首先获得一个文件句柄。File file new File(); file即为文件句柄。两人之间连通电话网络了。接下来可以开始打电话了。通过这条线路读取甲方的信息&#xff1a;new FileInputStream(file) 目前这个信息已经读进来内存当中了。…

UE4 远程调用函数

1. Multicast 如果在服务器端调用&#xff0c;则先在服务器执行此函数&#xff0c;然后自动依次在所有客户端执行此函数。 如果在客户端调用&#xff0c;则只在此客户端执行&#xff0c;服务器和其它客户端都不执行。 2. RunOnServer 不管是服务器端还是客户端&#xff0c;都可…

python有监督神经网络程序实例_学习编程|监督学习是什么?怎么用?Python机器学习基础笔记二...

一、机器学习基础概念1.机器学习分类机器学习分为&#xff1a;监督学习、无监督学习、半监督学习等几类。(1)监督学习&#xff1a;从给定的训练数据集中学习出一个函数&#xff0c;当新的数据到来时&#xff0c;可以根据这个函数预测结果。监督学习的训练集要求是包括输入和输出…

.net开发微信公众平台

一、说明&#xff1a;公众平台信息接口为开发者提供了一种新的消息处理方式&#xff0c;只有申请成为开发者后&#xff0c;你才能使用公众平台的开发功能&#xff0c;在这里你需要填写一个URL和一个Token&#xff0c;这两项信息也需要你拥有自己的服务器&#xff08;外网服务器…

python有趣函数_python中有趣的函数

filter(function, sequence)&#xff1a;对sequence中的item依次执行function(item)&#xff0c;将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)返回&#xff1a;>>> def f(x): return x % 2 ! 0 and x % 3 ! 0>>> filter(f, range…

使用客户端对象模型读取SharePoint列表数据

使用客户端对象模型读取SharePoint列表数据 客户端对象模型提供了强有力的方式&#xff0c;从远程客户端应用程序管理列表。1. 管理员身份打开VS&#xff0c;新建项目Windows窗体应用程序&#xff0c;命名ReadSPListData&#xff0c;确保选择.NET Framework 3.5。2. 添加控件。…

UE4 获得player id

获得Player ID 获得Player 位置 获得所有PlayerId

python学习---语法

python script常见格式: import sys #导入sys模块 from util import * #从util模块导入全部需要的变量,函数等 module[webview,content_shell,chrome] #列表数据结构 number(1,2,5,6,9) #元祖数据结构,不可改变 转载于:https://www.cnblogs.com/hanying/p/3792601.html