ue5 异步 Task

ue 5.8

A首推UE::Tasks::Launch

5.0版本引入,线程来源TaskGraph Worker pool,只能在work线程上运行,返回TTask<T>,并且能Cancel.

深层代码 俩系统都有不同的 FTaskBase,只能命名空间不一样

主要规则

  1. ❌不要在 Task Worker 线程直接操作 UObject:GC、对象销毁会直接 crash。
  2. ❌不要主线程 Tick 轮询IsCompleted()模拟通知,高耗 CPU。
  3. ❌不要捕获裸 UObject 指针,一律TWeakObjectPtr
  4. ❌不要在 Task 内部调用Sleep(),会占用工作线程池,挤压其他任务调度。
  5. ✅想要 "主动通知",不要用 Wait;Wait 是同步等待,不是回调通知。

task 结束 通知问题

方案 回调运行线程 可操作 UObject 是否阻塞 适用场景
任务体内尾部执行 Worker 不阻塞 纯 C++ 数据计算完成通知
任务内 Async 切 GameThread GameThread 不阻塞 绝大多数游戏业务、UI、Actor 更新
FPipe 链式任务 Worker 不阻塞 多步串行后台任务流
Wait / GetResult() 当前线程 看调用位置 ✅阻塞 单元测试、加载阶段同步等待,禁止游戏主线程

举例

无通知

cpp 复制代码
FTask MyTask = Launch(UE_SOURCE_LOCATION, []()
{
    // 后台耗时计算
    int32 Result = HeavyCalc();

    // ==========任务完成主动通知(当前仍在工作线程)==========
    UE_LOG(LogTemp, Log, TEXT("Task done, result=%d"), Result);
    
    // 注意:这里不能访问UObject!
});

AsyncTask切回Gamethread 最好的方法

cpp 复制代码
#include "Async/Async.h"

void Example_AsyncNotify(AActor* MyActor)
{
    TWeakObjectPtr<AActor> WeakActor(MyActor); // 必须弱指针,防止对象销毁悬空

    Launch(UE_SOURCE_LOCATION, [WeakActor]()
    {
        //=====后台线程耗时工作=====
        float ComputeValue = ExpensiveWork();

        //=====完成后主动通知,切回GameThread=====
        AsyncTask(ENamedThreads::GameThread, [WeakActor, ComputeValue]()
        {
            if (!WeakActor.IsValid()) return;
            // GameThread上下文,可以安全操作Actor、UObject、蓝图委托
            UE_LOG(LogTemp, Log, TEXT("回到游戏线程,计算结果=%f"), ComputeValue);
        });
    });
}

FPipe 任务管道,链式执行(依赖式通知)

FPipe构建任务链:前一个任务结束,自动调度下一个任务;所有任务在同一个 Pipe 串行执行,不会并发争抢资源。 适合:把 "后台工作" 与 "完成后处理" 拆成两个任务,形成完成通知链路。

cpp 复制代码
FPipe Pipe;

Launch(UE_SOURCE_LOCATION, [](){
    UE_LOG(LogTemp, Log, TEXT("Step1: heavy work"));
}, Pipe);

// 前一个任务结束自动跑这个,作为完成回调;全部跑在worker线程
Launch(UE_SOURCE_LOCATION, [](){
    UE_LOG(LogTemp, Log, TEXT("Step2: notify, previous task finished"));
}, Pipe);
  • FPipe 保证同一管道任务 FIFO 串行执行,前一个完成才跑下一个。
  • 续任务依然在 Worker 线程,如果要操作 UObject,内部再套一层AsyncTask(GameThread)切回去。

封装委托回调(比较笨,不推荐)

cpp 复制代码
DECLARE_DELEGATE_OneParam(FOnTaskComplete, int32);

void RunWorkWithCallback(FOnTaskComplete OnComplete)
{
    Launch(UE_SOURCE_LOCATION, [OnComplete]()
    {
        int32 Res = HeavyCalc();
        AsyncTask(ENamedThreads::GameThread, [OnComplete, Res]()
        {
            OnComplete.ExecuteIfBound(Res);
        });
    });
}

靠Wait或者GetResult 阻塞当前线程获得结果(完全不推荐)

B: TGraphTask / FFunctionGraphTask

ue4自带的,引擎底层原生异步系统,牛逼之处是能指定线程来运行

FFunctionGraphTask 属于 UE TaskGraph(任务图系统) ,核心用途:把一个可执行函数 / 回调封装成任务,投递到 TaskGraph 线程池执行,支持立即执行、延迟调度、依赖关系、指定执行线程,大量用于动画、物理、渲染、Slate、AsyncTask、蓝图编译、异步计算。

关键底层机制 & 坑点(高频踩坑)

  1. 生命周期风险:捕获 UObject 一定要小心 TFunction 是异步执行,捕获裸指针 UObject,如果对象中途 GC 销毁,直接野指针崩溃
  • 安全做法:捕获 TWeakObjectPtr<UXXX>,回调内部先 IsValid () 校验。
  • ❌禁止:[MyUObject](){ MyUObject->DoSomething(); } 裸捕获 UObject。
  1. 线程约束
  • ENamedThreads::GameThread:任务只会在游戏主线程跑,可以安全访问 UObject、蓝图对象。
  • AnyThread / Background绝对不能直接操作 UObject,UObject 非线程安全。
  1. ESubsequentsMode::FireAndForget 设置之后:GetCompletionEvent() 返回无效引用,外部不能依赖该任务。适合不需要后续依赖、纯点火即忘的任务,内存开销略小。
  2. UE::Tasks(新 Task 系统)对比
  • FFunctionGraphTask老 TaskGraph ,基于 FGraphEvent,擅长复杂多任务依赖链,很多引擎内部模块仍大量在用。
  • UE::Tasks::Launch():UE5 新任务库,API 更现代,但底层一部分仍然复用 TaskGraph。
  1. 不要保存返回的 FFunctionGraphTask* 指针! 引擎内部任务完成后自动回收该对象;保存裸指针后续访问是野指针。只能保存 FGraphEventRef(智能引用)
  2. 调度不等于立刻执行CreateAndDispatchWhenReady把任务提交到队列 ;如果前置依赖没完成,任务会挂起,不会马上跑。名字里 WhenReady 含义:就绪(全部 prerequisites 完成)之后才调度执行

用法

1、FFunctionGraphTask::CreateAndDispatchWhenReady

可以直接 lamabada

cpp 复制代码
FFunctionGraphTask::CreateAndDispatchWhenReady(
    []()
    {
        UE_LOG(LogTemp, Log, TEXT("在后台线程执行耗时计算"));
    },
    {}, // 无前置依赖
    ENamedThreads::AnyBackgroundThreadNormalTask
);
cpp 复制代码
TArray<FGraphEventRef> Prereqs;
// ...填充Prereqs为前面异步任务的GetCompletionEvent()

FFunctionGraphTask::CreateAndDispatchWhenReady(
    []()
    {
        check(IsInGameThread());
        // 回到游戏线程操作UObject
    },
    Prereqs,
    ENamedThreads::GameThread
);

结束的时候 消息通知

cpp 复制代码
FFunctionGraphTask::CreateAndDispatchWhenReady(
    []()
    {
        //===== 异步耗时工作 =====
        DoHeavyWork();

        //===== 任务结束通知(当前任务线程)=====
        UE_LOG(LogTemp, Log, TEXT("任务已经完成"));

        // 如果要在GameThread触发蓝图/对象回调
        FFunctionGraphTask::CreateAndDispatchWhenReady(
            [](){
                check(IsInGameThread());
                // Broadcast 委托、调用UObject接口
            },
            {},
            ENamedThreads::GameThread
        );
    },
    {},
    ENamedThreads::AnyBackgroundThreadNormalTask
);
cpp 复制代码
TArray<FGraphEventRef> Prereqs;
// ...填充Prereqs为前面异步任务的GetCompletionEvent()

FGraphEventRef  Task1= FFunctionGraphTask::CreateAndDispatchWhenReady(
    []()
    {
        UE_LOG(LogTemp, Log, TEXT("在后台线程执行耗时计算"));
    },
    {}, // 无前置依赖
    ENamedThreads::AnyBackgroundThreadNormalTask
);


Prereqs.Add(Task1);

	auto RemoveTrackableTask = FSimpleDelegateGraphTask::FDelegate::CreateRaw(this, &FRemoteSessionARSystemChannel::ReceiveRemoveTrackable_GameThread, UniqueId);
	FSimpleDelegateGraphTask::CreateAndDispatchWhenReady(RemoveTrackableTask, GET_STATID(STAT_ARSystemChannel_Receive), Prereqs, ENamedThreads::GameThread);

2、FSimpleDelegateGraphTask::CreateAndDispatchWhenReady

3、FDelegateGraphTask::CreateAndDispatchWhenReady

项目 FFunctionGraphTask FDelegateGraphTask FSimpleDelegateGraphTask
入参类型 TUniqueFunction<...> FGraphTaskDelegate(双参委托) FSimpleDelegate(零参委托)
回调签名 void()void(ENamedThreads::Type, FGraphEventRef&) void(ENamedThreads::Type, const FGraphEventRef&) void(),完全无参数
Lambda 捕获局部变量 ✅支持,move 捕获 ❌委托本身不能存闭包;只能 BindUObject/BindRaw ❌不支持
回调拿到任务 / 线程信息 ✅重载版本支持 ✅原生支持,回调直接带参数 ❌拿不到
UObject 安全检测 无,靠 lambda 内部 TWeakObjectPtr 委托自带 UObject 句柄有效性检测 委托自带 UObject 句柄有效性检测
底层实现 TGraphTask<TFunctionGraphTaskImpl> TGraphTask<FDelegateGraphTask> TGraphTask<FDelegateGraphTask>(上层封装)
典型使用场景 现代 C++,lambda,需要捕获栈变量;渲染 / 业务 TaskGraph 任务 已有双参委托;回调内部需要知道当前线程、任务 Event 老引擎代码,已有 FSimpleDelegate,只做简单回调

C: Async\AsyncTask

基于 GraphTask的高级封装 也非常常用

Async 是有 Future 和 Promis的 是可以不同线程 通讯的 很方便 传递值

cpp 复制代码
	AsyncTask(ENamedThreads::AnyThread, [this, NormalAssetData, AssetMetaData, LocationOffset]() {
		AsyncNormalImportCache(NormalAssetData, AssetMetaData, LocationOffset);
	});
cpp 复制代码
		TFuture<void> FinalizeTask = Async(EAsyncExecution::Thread, [&IoStoreWriterContext]()
		{
			IoStoreWriterContext->FinalizeWrites();
		});

todo 将来需要讲解的 太多了

FAsyncTask/FAutoDeleteAsyncTask

ParallelFor

FRunnableThread

IAsyncReadFileHandle

仅仅在GameThread的 异步类型

UAsyncAction

Timer / LatentAction

GameplayTask

相关推荐
曼巴UE53 天前
Unlua UE5.3 调试
ue5·lua·unlua
远离UE45 天前
UE5 母材质 编写笔记
笔记·ue5·材质
远离UE45 天前
UE5 HLSLTranslator 与 ShaderCompiler
ue5
1204157137 肖哥6 天前
UE5.7 场景打光管线流程_【雪山】
ue5
weixin_404679316 天前
虚幻5 替代小白人角色的完整逻辑
ue5
1204157137 肖哥9 天前
UE5.7 灯光详解
ue5
狂云歌10 天前
AI写UE的3Dgame
人工智能·3d·ue5·游戏程序
日月云棠14 天前
UE5源码分析之Editor——Blutility模块全面分析
ue5
日月云棠14 天前
UE5源码分析之Editor——BehaviorTreeEditor模块全面分析
ue5