UE5线程进阶(2-1):枚举类EAsyncExecution,作业类TAsyncRunnable、TAsyncQueuedWork,及全局线程函数 Async(..),及线程调用的 4 种方法总结

(49) 这是 async 函数的第一个参数 :

(50) 老师的举例讲解

(51)记录一些源代码 ,以下这俩全局函数的作用,是在单线程的操作系统上,启动任务函数的执行,并赋值 promise :

cpp 复制代码
/**  对于不支持多线程的操作系统,由本函数完成 promise 的赋值
 * Template for setting a promise value from a callable.
 */
template<typename ResultType, typename CallableType>
inline void SetPromise(TPromise<ResultType>& Promise, CallableType&& Callable)
{
	Promise.SetValue(Forward<CallableType>(Callable)());
}

template<typename CallableType>
inline void SetPromise(TPromise<void>& Promise, CallableType&& Callable)
{
	Forward<CallableType>(Callable)();
	Promise.SetValue();
}

(52) 以下这个枚举类,也要用到,作为类 TAsyncGraphTask 的数据成员

cpp 复制代码
namespace ENamedThreads
{
	enum Type : int32
	{
		UnusedAnchor = -1,
		/** The always-present, named threads are listed next **/
		RHIThread,
		GameThread,
		// The render thread is sometimes the game thread and is sometimes the actual rendering thread
		ActualRenderingThread = GameThread + 1,
		// CAUTION ThreadedRenderingThread must be the last named thread, insert new named threads before it

		/** not actually a thread index. Means "Unknown Thread" or "Any Unnamed Thread" **/
		AnyThread = 0xff, 

		/** High bits are used for a queue index and priority**/

		MainQueue =			0x000,
		LocalQueue =		0x100,

		NumQueues =			2,
		ThreadIndexMask =	0xff,
		QueueIndexMask =	0x100,
		QueueIndexShift =	8,

		/** High bits are used for a queue index task priority and thread priority**/

		NormalTaskPriority =	0x000,
		HighTaskPriority =		0x200,

		NumTaskPriorities =		2,
		TaskPriorityMask =		0x200,
		TaskPriorityShift =		9,

		NormalThreadPriority = 0x000,
		HighThreadPriority = 0x400,
		BackgroundThreadPriority = 0x800,

		NumThreadPriorities = 3,
		ThreadPriorityMask = 0xC00,
		ThreadPriorityShift = 10,

		/** Combinations **/
		GameThread_Local = GameThread | LocalQueue,
		ActualRenderingThread_Local = ActualRenderingThread | LocalQueue,

		AnyHiPriThreadNormalTask = AnyThread | HighThreadPriority | NormalTaskPriority,
		AnyHiPriThreadHiPriTask = AnyThread | HighThreadPriority | HighTaskPriority,

		AnyNormalThreadNormalTask = AnyThread | NormalThreadPriority | NormalTaskPriority,
		AnyNormalThreadHiPriTask = AnyThread | NormalThreadPriority | HighTaskPriority,

		AnyBackgroundThreadNormalTask = AnyThread | BackgroundThreadPriority | NormalTaskPriority,
		AnyBackgroundHiPriTask = AnyThread | BackgroundThreadPriority | HighTaskPriority,
	}; // 完结 enum Type : int32
	//.......
};

(53) 这些类都与 async 函数在同一个头文件,关系应该有关联 :

++ 给出 runnable 类的 Run ()函数的定义

++ 补充

(54) 本 sync 函数的带注释的源码

cpp 复制代码
/**
 * Execute a given function asynchronously.
 *
 * Usage examples:
 *
 *	// using global function
 *		int TestFunc()
 *		{
 *			return 123;
 *		}
 *
 *		TUniqueFunction<int()> Task = TestFunc();
 *		auto Result = Async(EAsyncExecution::Thread, Task);
 *
 *	// using lambda
 *		TUniqueFunction<int()> Task = []()
 *		{
 *			return 123;
 *		}
 *
 *		auto Result = Async(EAsyncExecution::Thread, Task);
 *
 *
 *	// using inline lambda
 *		auto Result = Async(EAsyncExecution::Thread, []() {
 *			return 123;
 *		}
 *
 * @param CallableType         The type of callable object.  本模板函数的模板参数
 * @param Execution            The execution method to use, i.e. on Task Graph or in a separate thread.
 * @param Function             The function to execute.
 * @param CompletionCallback   An optional callback function that is executed when the function completed execution.
 * @return A TFuture object that will receive the return value from the function.
 */
template<typename CallableType>
auto Async(EAsyncExecution Execution, CallableType&& Callable, TUniqueFunction<void()> CompletionCallback = nullptr) 
-> TFuture<decltype(Forward<CallableType>(Callable)())>   // 可见,本函数的返回值是 TFuture<U> 类型
{
	using ResultType = decltype(Forward<CallableType>(Callable)());            // 函数的返回值
	TUniqueFunction<ResultType()> Function(Forward<CallableType>(Callable));   // 要在线程里执行的函数
	TPromise<ResultType>          Promise(MoveTemp(CompletionCallback));       // promise,并带上了回调函数
	TFuture<ResultType>           Future = Promise.GetFuture();                // 这就是本函数的返回值

	switch (Execution)
	{
	case EAsyncExecution::TaskGraphMainThread:
		// fallthrough
	case EAsyncExecution::TaskGraph:
		{
			TGraphTask<TAsyncGraphTask<ResultType>>::CreateTask().ConstructAndDispatchWhenReady(
				MoveTemp(Function), MoveTemp(Promise), 
				Execution == EAsyncExecution::TaskGraph ? ENamedThreads::AnyThread : ENamedThreads::GameThread);
		}
		break;
	
	case EAsyncExecution::Thread:
		if (FPlatformProcess::SupportsMultithreading())  // 操作系统支持多线程
		{
			TPromise<FRunnableThread*> ThreadPromise;  // 本 TPromise 里封装了运行函数的线程的内存地址

			TAsyncRunnable<ResultType>* Runnable = new TAsyncRunnable<ResultType>(
					MoveTemp(Function), MoveTemp(Promise), ThreadPromise.GetFuture());
		//  TAsyncRunnable(TUniqueFunction<ResultType()>&& InFunction, TPromise<ResultType>&& InPromise, 
		//					TFuture<FRunnableThread*>&& InThreadFuture)

			const FString TAsyncThreadName = FString::Printf(TEXT("TAsync %d"), FAsyncThreadIndex::GetNext()); 
			// 给线程生成一个不会重复的名字

			FRunnableThread* RunnableThread = FRunnableThread::Create(Runnable, *TAsyncThreadName);  
			// 创建线程,任务函数要被执行了

			check(RunnableThread != nullptr);  // enum class FRunnableThread::ThreadType { Real, Fake, Forkable }
			check(RunnableThread->GetThreadType() == FRunnableThread::ThreadType::Real);

			ThreadPromise.SetValue(RunnableThread);  
			// 给这个 promise 填写上面生成的新线程的地址。这是 TAsyncRunnable 的需要的参数
		}
		else
		{
			SetPromise(Promise, Function);
		}
		break;

	case EAsyncExecution::ThreadIfForkSafe:
		if (FPlatformProcess::SupportsMultithreading() || FForkProcessHelper::IsForkedMultithreadInstance())
		{
			TPromise<FRunnableThread*> ThreadPromise;
			TAsyncRunnable<ResultType>* Runnable = new TAsyncRunnable<ResultType>(MoveTemp(Function), MoveTemp(Promise), ThreadPromise.GetFuture());

			const FString TAsyncThreadName = FString::Printf(TEXT("TAsync %d"), FAsyncThreadIndex::GetNext());
			FRunnableThread* RunnableThread = FForkProcessHelper::CreateForkableThread(Runnable, *TAsyncThreadName);

			check(RunnableThread != nullptr);
			check(RunnableThread->GetThreadType() == FRunnableThread::ThreadType::Real);

			ThreadPromise.SetValue(RunnableThread);
		}
		else
		{
			SetPromise(Promise, Function);
		}
		break;

	case EAsyncExecution::ThreadPool:  // 看来是交给全局线程池 GThreadPool 来执行本作业
		if (FPlatformProcess::SupportsMultithreading())
		{
			check(GThreadPool != nullptr);
			GThreadPool->AddQueuedWork(new TAsyncQueuedWork<ResultType>(MoveTemp(Function), MoveTemp(Promise)));
		}
		else
		{
			SetPromise(Promise, Function);
		}
		break;

#if WITH_EDITOR
	case EAsyncExecution::LargeThreadPool:
		if (FPlatformProcess::SupportsMultithreading())
		{
			check(GLargeThreadPool != nullptr);
			GLargeThreadPool->AddQueuedWork(new TAsyncQueuedWork<ResultType>(MoveTemp(Function), MoveTemp(Promise)));
		}
		else
		{
			SetPromise(Promise, Function);
		}
		break;
#endif

	default:
		check(false); // not implemented yet!
	}

	return MoveTemp(Future); // TFuture(TFuture&&) = default; 移动构造函数,本 async 函数执行完毕后,未必拿到了函数结果
}   // future.get() 会触发线程的睡眠等待。本函数只是触发了新线程的创建与执行。

(55)这里总结下 UE 里创建多线程的,目前学到的 4 种方法

a- 继承 FRunnable 接口,再创建 FRunnableThread 的子类对象运行 FRunnable 中的 Run ( ... ) 函数;

b- 使用线程池 FQueuedThreadPool 创建线程,继承自 IQueuedWork 定义作业,并添加给线程池执行;

c- 不再显式使用线程,自定义作业 FMySimpleWork, 定义其中的 4 个函数,

作为 FAsyncTask 或 FAutoDeleteAsyncTask 的模板参数;

再用成员函数 StartBackgroundTask(...) 等启动作业的执行。

d- 使用 全局 Async(...) 函数,定义自己要执行的函数即可,还可以有要拿到的返回值。

(56)全局 Async(...) 函数 的简化版定义

(57)本函数的使用举例

++ 演示回调函数

(58)

谢谢

相关推荐
狂云歌12 小时前
AI写UE的3Dgame
人工智能·3d·ue5·游戏程序
日月云棠4 天前
UE5源码分析之Editor——Blutility模块全面分析
ue5
日月云棠4 天前
UE5源码分析之Editor——BehaviorTreeEditor模块全面分析
ue5
日月云棠4 天前
UE5源码分析之Editor——AssetTagsEditor模块全面分析
ue5
远离UE45 天前
UE5 显存 虚拟内存 深入学习笔记
笔记·学习·ue5
日月云棠6 天前
UE5源码分析之Editor——AnimationSettings模块全面分析
ue5
远离UE46 天前
UE5 SF_VertexShader 如何使用
ue5
电子云与长程纠缠6 天前
UE5 Lyra PocketWorld进行3D内容UI预览 - 上
开发语言·学习·3d·ue5·游戏引擎
电子云与长程纠缠7 天前
UE5 Lyra PocketWorld进行3D内容UI预览 - 下
开发语言·学习·游戏·ui·ue5
远离UE47 天前
UE5 GPU内部异步
ue5