首先查阅了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

UE4--多线程的实现方式相关推荐

  1. Java 多线程的基本方式

    Java 多线程的基本方式 基础实现两种方式: 通过实现Callable 接口方式(可得到返回值):

  2. 多线程的实现方式_Java中线程的状态及多线程的实现方式

    线程的状态 线程状态图: 说明: 线程共包括以下5种状态.1. 新建状态(New) : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread().2. 就绪状 ...

  3. Java多线程的实现方式-Thread 类,Runnable 接口

    在 Java 的 JDK 开发包中,已经自带了对多线程技术的支持,可以方便地进行多线程编程.实现多线程编程的方式主要有两种:一种是继承 Thread 类,另一种是实现 Runnable 接口.下面详细 ...

  4. java 多线程状态_总结Java中线程的状态及多线程的实现方式

    线程的状态线程状态图: 说明: 线程共包括以下5种状态. 1. 新建状态(New) : 线程对象被创建后,就进入了新建状态.例如,Thread thread = new Thread(). 2. 就绪 ...

  5. java多线程的实现方式_Java 多线程(一)——多线程的实现方式

    一.前言 Java 异常的处理方式与自定义异常 我们已经讲完了,从今天开始我们来学习多线程. 二.与多线程相关的概念 2.1.并发与并行并发:指两个或多个事件在同一个时间段内发生,具体如下图所示: 并 ...

  6. java 多线程跑数据_java——多线程的实现方式、三种办法解决线程赛跑、多线程数据同步(synchronized)、死锁...

    多线程的实现方式:demo1.demo2 demo1:继承Thread类,重写run()方法 packagethread_test;public class ThreadDemo1 extendsTh ...

  7. 多线程的创建方式 你会优先选择哪一种_Python多线程入门到放弃

    在生产者消费者的中, 我们经常会遇到任务调度的情况, 这时候常用的解决方案就是使用多线程来解决相关的调度问题. 多线程扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务. 线程(Thread) ...

  8. UE4的JSON读写方式二

    声明:所有权利保留. 转载必须说明出处:http://blog.csdn.net/cartzhang/article/details/43794409 Json的Writer博客地址: http:// ...

  9. js的定时器 实现页面展示的异步刷新 多线程同步实现方式 附进度条js代码

    先简单介绍下两个场景 结果迅速能出来,采用多线程的同步方式 如果逻辑处理比较快,3-5秒任务就会执行完毕,你也可以采用同步多线程同步的方式,代码如下: 数据校验使用到的线程实例: if(null!=c ...

  10. 集合到文件 文件到集合 点名器 集合到文件数据排序版 标准输入流 打印流 对象序列化流 对象返序列化流 Properties 游戏次数 进程和线程 线程 多线程的实现方式 设置和获取线程名称

    文章目录 集合到文件 文件到集合 点名器 集合到文件数据排序版 标准输入流 打印流 对象序列化流 对象返序列化流 Properties 游戏次数 进程和线程 线程 多线程的实现方式 设置和获取线程名称 ...

最新文章

  1. 在.NET中不安装Office使用EPPlus生成带图表(Chart)的Excel报表
  2. pyhanlp 命名实体识别
  3. 团队开发项目--校园知网 nabcd 需求分析
  4. java word 饼图_[Java教程]echarts标准饼图解读(一)——基本配置demo
  5. truncate table语句和delete table语句的区别
  6. java objective-c,Objective-C基础教程学习笔记(附录)从Java转向Objective-C
  7. 鼠标在滑块上滚轮控制_直线导轨(滚轮导轨)与线轨(滚珠导轨)的优劣势对比...
  8. gimp 抠图_gimp抠图教程:gimp快速实现抠图效果
  9. 大学计算机ppt制作步骤,PPT制作教程步骤方法_PPT制作技巧教程快捷键_PPT制作基础教程...
  10. SSH 免密码/免用户名/免IP登录云服务器实践
  11. CVE-2019-0708 微软补丁更新
  12. 有限维空间上的线性算子
  13. 用QQ邮箱接收网易163企业邮箱的邮件
  14. vs2012运行c语言出现:无法查找或打开 PDB 文件
  15. cURL – POST请求示例
  16. 压力测试-Jmeter自动化测试教程
  17. 小米路由器R1D改造记录-开放ssh
  18. OpenGL-雾Fog-实例
  19. 猫爪杯爆红背后的套路,你上勾了吗?
  20. 【软件测试】自动化测试战零基础教程——Python自动化从入门到实战(六)

热门文章

  1. linkedhashmap 顺序_LinkedHashMap 源码详细分析(JDK1.8)
  2. java ReentrantLock 使用
  3. 2019CCPC湖南全国邀请赛-Chika and Friendly Pairs- 莫队+树状数组+离散化
  4. SpingBoot+Mybaits+Vue,更新学习
  5. 洛谷 P1852 奇怪的字符串
  6. 【4.0】jdbcTemplate
  7. [Selenium] 最大化或自定义浏览器的大小
  8. ASP.NET WebForm中使用WebApi
  9. 安装用户debian7安装oracle11g
  10. 不同职业的面试着装技巧。