关于虚幻多线程的学习
1 先去复习了下C++的多线程异步和单线程异步
关于创建虚幻中的三种多线程实现:
. 继承FRunnable接口创建单个线程。
. 直接创建AsyncTask来调用线程池里空闲的线程。
. 用过TaskGraph系统来异步完成一些自定义任务。
2 看了下虚幻中,用Tick模拟局部异步,算是单线程异步
3 根据官方文档,继承FRunnable类来进行虚幻中的多线程使用
关于Runnable的方式,观看大佬Jerish的文章后,进行补充:
4 使用AsyncTask的多线程(AsyncWork.h中有基础例子)
4.4 关于AsyncTask的FScopeLock和FNonAbandonableTask以及AsyncTask与转发构造
4.4.1 FScopeLock是UE提供的一种基于作用域的锁,思想类似RAII机制。在构造时对当前区域加锁,离开作用域时执行析构并解锁。UE里面有很多带有“Scope”关键字的类,如移动组件中的FScopedMovementUpdate,Task系统中的FScopeCycleCounter,FScopedEvent等,他们的实现思路是类似的。
4.4.2 FNonAbandonableTask
继承FNonAbandonableTask的Task不可以在执行阶段终止,即使执行Abandon函数也会去触发DoWork函数。
4.4.3 创建自定义任务的方式如下
FAsyncTask<ExampleAsyncTask>*MyTask= new FAsyncTask<ExampleAsyncTask>(5);
括号里面的5会以参数转发的方式传到的ExampleAsyncTask构造函数里面,这一步涉及到C++11的右值引用与转发构造。
5 TaskGraph系统的多线程
Task Graph 系统是UE4一套抽象的异步任务处理系统,可以创建多个多线程任务,指定各个任务之间的依赖关系,按照该关系来依次处理任务。
TaskGraph分为两种任务,有命名任务和无具体命名任务,其中有命名任务有5种Stat/Render/Game/RHI/Audio/ActualRendering。有命名的任务一旦放入执行队列中就不能随意调整任务了并且是由FThreadTaskQueue来安排处理执行顺序;无具体命名任务也就是自定义任务,是可以随意调整任务的顺序队列的。
TaskGraph系统中任务和任务之间是有依赖关系的,彼此可能在不同的线程中,由不同的触发事件的完成来进行任务的执行。如图:
其中在创建任务时,就会选择好任务的触发事件
FGraphEventRef Join=TGraphTask<FVictoryTestTask>::CreateTask(NULL, ENamedThreads::GameThread).ConstructAndDispatchWhenReady();
CreateTask的第一个参数就是该任务依赖事件数组(这里为NULL),如果传入一个事件数组的话,那么当前任务就会通过SetupPrereqs函数设置这些依赖事件,并且在所有依赖事件都触发后再将该任务放到任务队列里面分配给线程执行。
当执行CreateTask时,会通过FGraphEvent::CreateGraphEvent()构建一个新的后续事件,再通过函数ConstructAndDispatchWhenReady返回。这样我们就可以在当前的位置执行:
FTaskGraphInterface::Get().WaitUntilTaskCompletes(Join, ENamedThreads::GameThread_Local);
让当前线程等待该任务结束并触发事件后再继续执行,当前面这个事件完成后,就会调用DispatchSubsequents()去触发他后续的任务。WaitUntilTaskCompletes函数的第二个参数必须是当前的线程类型而且是带名字的。
不要在非GameThread线程内执行下面几个操作:
- 不要 Spawn / Modify/ delete UObjects or AActors
- 不要使用定时器 TimerManager
- 不要使用任何绘制接口,例如 DrawDebugLine
最后附上观看的大佬的地址:https://zhuanlan.zhihu.com/p/38881269