【UE5 C++课程系列笔记】25——多线程基础——FGraphEventRef的简单使用
目录
概念
使用示例1
使用示例2
概念
FGraphEventRef 本质上是对一个异步任务或者一组相关异步任务在虚幻引擎任务图系统中的一种引用(reference)。虚幻引擎的任务图系统用于高效地调度和管理各种异步任务,协调它们的执行顺序以及处理任务之间的依赖关系等,而 FGraphEventRef 就是开发者与这个任务图系统交互的一个关键 “接口”,通过它可以实现对任务的创建、跟踪以及依赖管理等操作。
使用示例1
先创建并执行一个简单的异步任务SimpleEvent,等待其完成后,再批量创建 20 个异步任务存入 SimpleEventArray 中,最后等待这 20 个任务全部完成,以此体现任务图系统在协调多线程任务的执行顺序以及处理任务之间依赖关系的作用。
代码如下所示:
void UThreadSubsystem::GraphEvent()
{FGraphEventRef SimpleEvent = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEvent Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEvent End"));});check(!SimpleEvent->IsComplete());SimpleEvent->Wait();FGraphEventArray SimpleEventArray;for (size_t i = 0; i < 20; i++){SimpleEventArray.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([this,i]() {FString Info1 = FString::Printf(TEXT("SimpleEvent Start -- %d"), i);PrintLogInThread(Info1);FPlatformProcess::Sleep(i%3);FString Info2 = FString::Printf(TEXT("SimpleEvent End -- %d"), i);PrintLogInThread(Info2);}));}FTaskGraphInterface::Get().WaitUntilTasksComplete(SimpleEventArray);
}void UThreadSubsystem::PrintLogInThread(FString Info)
{AsyncTask(ENamedThreads::GameThread, [Info]() {UE_LOG(LogTemp, Warning, TEXT("ThreadLog:[%s]"), *Info);});
}

在第147行代码~151行代码中,通过 FFunctionGraphTask::CreateAndDispatchWhenReady 函数创建一个异步任务,并将返回的 FGraphEventRef 对象赋值给 SimpleEvent。在该异步任务的 lambda 表达式中,通过调用 FPlatformProcess::Sleep(3); 让线程休眠 3 秒来模拟任务执行过程中的耗时操作。
第152行代码用于检查在创建任务后,任务是否立即就完成了(正常情况下刚创建的任务应该还未执行完,所以这里预期 IsComplete 返回 false),如果出现不符合预期的情况(即任务已经完成),check 宏会触发断言失败,有助于在开发阶段发现可能的逻辑错误。
第153行代码调用 SimpleEvent->Wait(); 会使当前线程阻塞,一直等待这个异步任务执行完毕,确保后续代码在该任务完成后才继续执行,实现了简单的任务同步机制,保证了任务执行顺序上的先后关系。
第155行代码创建了一个 FGraphEventArray 类型的数组 SimpleEventArray,用于存放多个 FGraphEventRef 对象,每个对象对应一个异步任务。
第156~167行代码通过 for 循环创建20个异步任务
第169行代码通过调用 FTaskGraphInterface::Get().WaitUntilTasksComplete(); 函数,让当前线程阻塞等待,直到 SimpleEventArray 数组中存放的所有 20 个异步任务都执行完毕,实现了对多个并行任务的同步等待功能。
执行结果如下:

使用示例2
该示例主要利用虚幻引擎的任务图系统来创建多个异步任务,并通过设置任务之间的依赖关系,实现特定的任务执行顺序。要实现的任务执行顺序如下图所示,我们需要先执行TaskA,TaskA执行完毕后开始同时执行TaskA1和TaskB,TaskB执行完毕后再执行TaskC,在TaskA1和TaskC都执行完毕的基础上再执行TaskD。

代码如下所示:
void UThreadSubsystem::BatchGraphEvent()
{FGraphEventRef SimpleEventA = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEventA Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEventA End"));});FGraphEventRef SimpleEventB = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEventB Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEventB End"));}, TStatId{}, SimpleEventA);FGraphEventRef SimpleEventC = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEventC Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEventC End"));}, TStatId{}, SimpleEventB);FGraphEventRef SimpleEventA1 = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEventA1 Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEventA1 End"));}, TStatId{}, SimpleEventA);FGraphEventArray Prerequisite;Prerequisite.Add(SimpleEventA1);Prerequisite.Add(SimpleEventC);FGraphEventRef SimpleEventD = FFunctionGraphTask::CreateAndDispatchWhenReady([this]() {PrintLogInThread(TEXT("SimpleEventD Start"));FPlatformProcess::Sleep(3);PrintLogInThread(TEXT("SimpleEventD End"));}, TStatId{}, &Prerequisite);SimpleEventD->Wait();PrintLogInThread(TEXT("All Tasks Completed"));
}

第174~178行代码创建了一个异步任务 SimpleEventA
第180~184行代码创建了一个异步任务 SimpleEventB,并设置任务 SimpleEventB 设置了依赖于任务 SimpleEventA
第186~190行代码创建了一个异步任务 SimpleEventC,并设置任务 SimpleEventC 设置了依赖于任务 SimpleEventB
第192~196行代码创建了一个异步任务 SimpleEventA1,并设置任务 SimpleEventA1 设置了依赖于任务 SimpleEventA
第198~206行代码首先创建了一个 FGraphEventArray 类型的数组 Prerequisite,并将SimpleEventA1和 SimpleEventC 这两个 FGraphEventRef 对象添加到其中,以此表示一组前置任务条件。然后创建任务 SimpleEventD 时,将 Prerequisite 作为参数传入,意味着SimpleEventD依赖于 SimpleEventA1 和 SimpleEventC 这两个任务,只有当这两个任务都执行完成后,SimpleEventD才会开始执行。
第208行代码通过调用 SimpleEventD->Wait(); 让当前线程阻塞等待,直到任务SimpleEventD执行完毕。由于 SimpleEventD 依赖于前面多个任务,所以实际上是等待整个任务链上的所有相关任务都完成后,此阻塞才会解除。
执行结果如下:

如果要统计所有任务执行花费时间,可以通过添加如下代码实现


执行结果如下,总共花费12s,和预想的一样。


相关文章:
【UE5 C++课程系列笔记】25——多线程基础——FGraphEventRef的简单使用
目录 概念 使用示例1 使用示例2 概念 FGraphEventRef 本质上是对一个异步任务或者一组相关异步任务在虚幻引擎任务图系统中的一种引用(reference)。虚幻引擎的任务图系统用于高效地调度和管理各种异步任务,协调它们的执行顺序以及处理任务…...
计算机网络之---信号与编码
信号 在物理层,信号是用来传输比特流的物理量,它可以是电压、电流、光强度等形式,通常通过电缆、光纤或者无线信道等媒介传播。 信号主要分为以下两种类型: 模拟信号(Analog Signal):信号在时间…...
linux下用命令行给串口写数据和读数据
在 Linux 系统中,串口设备(如 /dev/ttyS3)可以通过命令行进行读写操作。您遇到的问题是因为 Bash 解释了命令行中的字符串 “dis vlan\n”,但并没有按预期向串口设备发送数据。你应该将数据通过重定向发送到串口设备。 下面是如何…...
【生物信息】如何使用 h5py 读取 HDF5 格式文件中的数据并将其转换为 NumPy 数组
data_mat h5py.File(args.data_file) x1 np.array(data_mat[X1]) x2 np.array(data_mat[X2]) if not args.no_labels: y np.array(data_mat[Y]) data_mat.close() 这段代码展示了如何使用 h5py 读取 HDF5 格式文件中的数据并将其转换为 NumPy 数组。以下是代码的详细解释&a…...
纯手工(不基于maven的pom.xml、Web容器)连接MySQL数据库的详细过程(Java Web学习笔记)
1 引言 最近读一些Java Web开发类的书籍时,发现书中的连接数据库的过程缺少了一些关键性的过程,这对初学者非常不友好。为此,本文将给出详细的连接MySQL数据库的过程,并且是纯手工,不依赖于pom.xml和Web容器ÿ…...
thingsboard通过mqtt设备连接及数据交互---记录一次问题--1883端口没开,到服务器控制面板中打开安全组1883端口
1,链接不上:原因是1883端口没开,到服务器控制面板中打开安全组1883端口 2,参考链接: https://blog.csdn.net/bujingyun8/article/details/120024788...
联邦学习中的LoRA:FedLoRA
联邦学习中的LoRA:FedLoRA 联邦学习中的LoRA(Low-Rank Adaptation of Large Language Models)是一种用于在联邦学习场景下对大型语言模型进行低秩适应和高效微调的方法。以下是其原理及示例说明: 原理 低秩矩阵分解:在联邦学习中,通常会涉及到对预训练的大型模型进行微…...
PyTorch reshape函数介绍
torch.reshape 是 PyTorch 用于改变张量形状的函数之一。它不会改变张量的数据,而是重新组织其元素以适应新的形状。 reshape 的使用 torch.reshape(input, shape) → Tensorinput:输入张量。shape:新形状,使用整数或 -1 指定各维…...
Linux内核 -- 邮箱子系统之`mbox_controller` 的 `txdone_irq` 用法
Linux Kernel 中 mbox_controller 的 txdone_irq 用法 1. txdone_irq 的作用 txdone_irq 是一个布尔类型字段,用来指示邮件框控制器是否支持通过中断通知传输完成事件。 如果设置为 true: 硬件会在数据传输完成后生成中断。内核中相应的中断处理程序会…...
Linux/Ubuntu/银河麒麟 arm64 飞腾FT2000 下使用 arm64版本 linuxdeployqt 打包Qt程序
文章目录 一、前言二、环境三、准备1、下载Linuxdeployqt源码2、下载Appimagetool-aarch64.AppImage四、编译linuxdeployqt1.配置环境变量2.编译linuxdeployqt五、安装patchelf六、配置Appimagetool七、打包Qt程序重要提示:测试启动应用八、其他九、最后一、前言 因为项目需要…...
Excel | 空格分隔的行怎么导入excel?
准备工作:windows,一个记事本程序和微软的Excel软件。 打开记事本,选中所有内容,按CtrlA全选,然后复制(CtrlC)。 在Excel中,定位到你想粘贴的单元格,按CtrlV进行粘贴。粘贴后,你会在…...
如何将某两个提交去掉父提交的合并
q: 在一个两个月前的分支,我想保持纯净,但是需要把另一个变化很大的分支只将某两个提交的变更同步过来,基于idea的git操作该怎么做 a: 其实很多人会一下想到cherry pick,这个确实方便,但是会将父提交连带合…...
Windows下安装最新版的OpenSSL,并解决OpenSSL不是当前版本的问题,或者安装不正确的问题
文章目录 1. 文章引言1.1 需求描述1.2 简单介绍1.3 支持平台1.4 源码地址1.5 组件介绍2. 下载OpenSSL3. 安装OpenSSL5. 查看安装目录6. 解决OpenSSL的错误1. 文章引言 1.1 需求描述 今天接到一需求,解密php加密后的数据,由于php使用 openssl_encrypt的方式加密,java也需要使…...
Django 社团管理系统的设计与实现
标题:Django 社团管理系统的设计与实现 内容:1.摘要 本文介绍了 Django 社团管理系统的设计与实现。通过分析社团管理的需求,设计了系统的架构和功能模块,并使用 Django 框架进行了实现。系统包括社团信息管理、成员管理、活动管理、财务管理等功能&…...
android compose 串口通信
1.添加依赖 implementation("io.github.xmaihh:serialport:2.1.1") 2.添加SerialHelper派生类 class SerialPortHelper(portName:String,baudRate:Int): SerialHelper(portName,baudRate) {var receivedDataBuffer mutableListOf<Byte>()override fun onDa…...
Cursor无限续杯——解决Too many free trials.
前情提要 我们都知道Cursor对新用户是有14天且500条免费限制的。 一般情况下,当14天过期,是可以注销账户再重新注册,这样就可以继续拥有14天的体验时长。 但是!!如果使用超过500次,Cusor就会把你的电脑I…...
网络传输层TCP协议
传输层TCP协议 1. TCP协议介绍 TCP(Transmission Control Protocol,传输控制协议)是一个要对数据的传输进行详细控制的传输层协议。 TCP 与 UDP 的不同,在于TCP是有连接、可靠、面向字节流的。具体来说,TCP设置了一大…...
我的前端面试笔记(React篇)
1.React16 的 Fiber 是什么 fiber是react16的一种数据结构,用来描述每一个React 组件或 DOM 节点,并采用双向链表的存储结构。遍历节点的方式采用迭代遍历(while循环),替代了以前的递归遍历。 fiber的优点 1ÿ…...
Qt 5.14.2 学习记录 —— 팔 QWidget 常用控件(3)
文章目录 1、cursor2、font3、toolTip4、focusPolicy5、styleSheeyt 1、cursor 改变鼠标光标形状。 在Qt Designer界面中,拖一个按钮过来,右边属性面用户可以自己改cursor属性。 代码方法,先拖一个按钮到界面上: #include <…...
HTTP协议和WebSocket协议
传统的HTTP协议是单向通信的,客户端若想要与服务器进行通信,首先需要通过三次握手与服务器建立TCP连接,然后再向服务器发送数据请求,当客户端收到服务器响应回来的数据后,就会断开TCP连接,每次数据请求都需…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
