UE4多线程

UE4 多线程

UE4虽然沿用了C++11的标准,但多线程并没有使用std::thread,而是自己封装了RunnableAsyncTask以及TaskGraph这三个可以供我们使用的线程。他们的本质相同但用法不同,使用Runnable可以自己创建复写线程函数的接口,AsyncTask是基于Runnable的一层封装,使用会更加方便。而TaskGraph则是基于任务的多线程(Task-Based),可以有效控制任务的顺序性。接下来将简单介绍这三种线程的使用。

1. Runnable

RunnableUE4中最基本的多线程,通过继承复写接口来使用。

FRunnable源码:

class CORE_API FRunnable
{
public:
	virtual bool Init()
	{
		return true;
	}
	virtual uint32 Run() = 0;
	virtual void Stop() { }
	virtual void Exit() { }

	virtual class FSingleThreadRunnable* GetSingleThreadInterface( )
	{
		return nullptr;
	}
	virtual ~FRunnable() { }
};

​ 其中函数的作用从名字就可以看出来了,必须要复写的接口就是Run(线程运行的逻辑函数)。

于是我们可以写下我们自己的派生类继承于FRunnable

class CPPDELEGATEANDSUBSYS_API FRunnableTest:public FRunnable
{
public:
   virtual bool Init()override;
   virtual uint32 Run() override;
   virtual void Exit()override;
};

创建方法:

FRunnableTest* threadPtr = new FRunnableTest();	//不参加GC需要自行delete,可以用智能指针进行管理
FRunnableThread::Create(threadPtr,TEXT("thread01"));

通过FRunnableThread这个真正的线程中的静态函数Create进行创建。

// Create参数
static FRunnableThread* Create(
class FRunnable* InRunnable,
const TCHAR* ThreadName,
uint32 InStackSize = 0,
EThreadPriority InThreadPri = TPri_Normal,
uint64 InThreadAffinityMask = FPlatformAffinity::GetNoAffinityMask(),
EThreadCreateFlags InCreateFlags = EThreadCreateFlags::None);

同时线程同步的问题,在UE4中的解决方案是,使用临界区变量、线程安全属性ESPMode::ThreadSefe

//临界区变量的定义以及声明
static FCriticalSection _LockSection;
FScopeLock Lock(&_LockSection);	

2. AsyncTask

AsyncTask是基于线程池的异步任务处理系统。和Runnable不同的是,Runnable在底层调用的是WindowsAPICreateThread,而AsyncTask是直接在UE给我们创建好的线程池中获得一个闲置的线程,使用AsyncTask我们需要继承于FNonAbandonableTask。字面理解无法被抛弃的任务,指的是在线程执行过程中无法被终止。

class  FMyAsyncTask : public FNonAbandonableTask
{
    // 可以自动销毁
	friend class FAutoDeleteAsyncTask<FMyAsyncTask>;
    // 需要手动销毁
    // friend class FAsyncTask<FMyAsyncTask>;
public:
    // 执行函数
	void DoWork();
    
    // 获取StatId
	FORCEINLINE TStatId GetStatId()const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FMyAsyncTask,STATGROUP_ThreadPoolAsyncTasks);
	}
};

其中我们必须要将FAutoDeleteAsyncTask或者FAsyncTask作为友元类,一个创建Task后需要手动删除,一个可以自动删除。

创建方式:

// FAutoDeleteAsyncTask作为友元类
FAsyncTask<FMyAsyncTask>*Task = new FAsyncTask<FMyAsyncTask>();
Task->StartBackgroundTask(); //后台运行,从线程池中调用
//Task->StartSynchronousTask(); //从当前线程运行

// FAsyncTask作为友元类
FAsyncTask<FMyAsyncTask>* Task = new FAsyncTask<FMyAsyncTask>();
Task->StartSynchronousTask();
if(Task->IsDone())
{
    UE_LOG(LogTemp,Warning,TEXT("Task is Done"));
}
// 模拟同步操作,主线程需要等待子线程完成任务之后才能继续执行。EnsureCompletion在代码底层封装的是信号量
Task->EnsureCompletion();
delete Task;
Task = nullptr;

其中如果使用FAsyncTask作为友元类,则需要自己手动删除。IsDone()判断任务有没有执行完成,EnsureCompletion()确保任务执行完成。

3. TaskGraph

TaskGrap于传统的基于线程(Thread-Based)的并行编程不同的是,他是基于任务(Task-Based)的并行编程。那么什么是基于任务的并行编程呢?可以简单慨括,将流程处理规划成"任务处理"。通过TaskGraph我们可以着重于某单个任务是需要并行还是需要串行,我们并不需要关心threading内部的问题,比如死锁、线程同步。引擎自会帮我们管理线程池等底层对象。

class FGraphTaskTest
{
public:
    // 获取StatId
	FORCEINLINE TStatId GetStatId()const
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(FGraphTaskTest,STATGROUP_TaskGraphTasks);
	}
	// 获取该Task运行的线程
	static ENamedThreads::Type GetDesiredThread()
	{
		return ENamedThreads::AnyThread;
	}
	// 获取Task依赖模式
	static ESubsequentsMode::Type GetSubsequentsMode()
	{
		return ESubsequentsMode::TrackSubsequents;
	}
    // Task逻辑函数
	void DoTask(ENamedThreads::Type CurrentThread,const FGraphEventRef& CompletionEventRef);
};

GetDesiredThread()函数可以设置Task运行在哪个线程里面。GetSubsequentsMode()设置Task的依赖性。

ESubsequentsMode::FireAndForget;	//没有依赖性
ESubsequentsMode::TrackSubsequents;	//依赖于子任务

通过依赖性可以实现任务的同步操作。DoTask()中传入的参数:1.当前任务运行的线程,2.FGraphEvent为当前任务的句柄,配合句柄可以对任务做出一些扩展操作。

创建方式:

static FConstructor CreateTask(const FGraphEventArray* Prerequisites = NULL, ENamedThreads::Type CurrentThreadIfKnown = ENamedThreads::AnyThread)
// 1.Prerequisites 前置任务数组
// 2.CurrentThreadIfKnown 运行线程
//1.通过ConstructAndDispatchWhenReady(延迟构造)进行构造 后面传入的参数是延迟构造的时间以及class的构造
FGraphEventRef Event = TGraphTask<FGraphTaskTest>::CreateTask(nullptr,ENamedThreads::AnyThread).ConstructAndDispatchWhenReady();
//2.通过ConstructAndHold进行构造(构造了但不会执行),需要weakup唤醒
TGraphTask<class FGraphTaskTest> *TaskPtr = TGraphTask<FGraphTaskTest>::CreateTask(nullptr,ENamedThreads::AnyThread).ConstructAndHold();
// 唤醒
TaskPtr->Unlock();
  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值