当前位置: 首页 > news >正文

【UE5 C++课程系列笔记】23——多线程基础——AsyncTask

 目录

概念

函数说明

注意事项

(1)线程安全问题

(2)依赖特定线程执行的任务限制

(3)任务执行顺序和时间不确定性

使用示例


概念

  AsyncTask 允许开发者将一个函数或者一段代码逻辑提交到特定的线程去执行,这样做的好处是可以避免阻塞当前线程(比如主线程),充分利用多线程的优势,实现并发执行任务,提升程序的响应性和整体性能。例如,在游戏开发中,可以将一些耗时的操作(如文件加载、网络通信、复杂的数据计算等)通过 AsyncTask 放到后台线程去执行,让主线程能够继续处理用户输入、渲染画面等对实时性要求较高的任务,防止游戏出现卡顿现象。

函数说明

  AsyncTask 函数原型如下:

template<typename ThreadType, typename FunctionType>
FAsyncTask<ThreadType, FunctionType> AsyncTask(ThreadType InThreadType, FunctionType&& InFunction);

  AsyncTask 函数接收两个参数:

(1)InThreadType 参数:这个参数用于指定要将任务提交到哪个线程去执行,通常是ENamedThreads枚举类型中的一个值。ENamedThreads 定义了虚幻引擎中一些常见的、有特定用途的线程类型,比如:

    ENamedThreads::AnyThread:表示任务可以在任意可用的线程中执行,引擎会根据当前的线程资源情况自动选择一个合适的线程来运行该任务。

     ENamedThreads::GameThread:专门用于执行和游戏逻辑紧密相关的任务,像处理游戏中的角色行为、更新游戏状态等大部分核心游戏逻辑通常都在游戏线程中执行。很多时候需要将一些其他线程中的操作结果反馈到游戏线程中(例如更新 UI 元素,因为 UI 的更新通常要求在游戏线程进行),就可以使用 AsyncTask 将相关任务提交到游戏线程来完成。

(2)InFunction 参数:这个参数是一个可调用对象(比如函数指针、lambda 表达式等),它定义了要在指定线程中实际执行的任务逻辑。可以将具体需要异步执行的代码封装在这个可调用对象里,例如一个简单的 lambda 表达式示例如下:

AsyncTask(ENamedThreads::BackgroundThread, []() {// 这里放置需要在后台线程执行的任务逻辑,比如加载资源文件FString FilePath = TEXT("Path/To/SomeResourceFile.txt");FString FileContent;FFileHelper::LoadFileToString(FileContent, *FilePath);// 可以继续添加对加载后的资源文件内容进行处理等其他逻辑
});

注意事项

(1)线程安全问题

        虽然 AsyncTask 提供了方便的异步任务执行机制,但在不同线程之间传递数据或者操作共享资源时,需要特别注意线程安全问题。例如,如果一个异步任务在后台线程中修改了某个共享变量,而主线程或者其他线程也会访问这个变量,就可能导致数据不一致、程序崩溃等并发访问冲突问题。解决办法通常是使用合适的多线程同步机制(如临界区 FCriticalSection 、互斥锁 FMutex 等)来保护对共享资源的访问确保在多线程环境下数据的一致性和操作的正确性。

        使用临界区 FCriticalSection的示例代码如下,通过如下方式来保护对共享变量 SharedVariable 的访问,保证在同一时刻只有一个线程能对其进行修改或读取操作,避免了并发冲突。

#include "CoreMinimal.h"
#include "HAL/CriticalSection.h"int SharedVariable = 0;
FCriticalSection CriticalSection;void BackgroundTaskFunction()
{FScopeLock Lock(&CriticalSection);SharedVariable++;
}void SomeFunction()
{AsyncTask(ENamedThreads::BackgroundThread, BackgroundTaskFunction);// 在主线程或者其他可能访问 SharedVariable 的线程中,同样需要加锁保护访问FScopeLock Lock(&CriticalSection);UE_LOG(LogTemp, Log, TEXT("SharedVariable value: %d"), SharedVariable);
}

(2)依赖特定线程执行的任务限制

         有些任务对执行的线程有严格要求,比如UI 更新操作通常要求在游戏线程中进行,如果错误地将这类任务提交到其他非游戏线程执行,可能会导致程序出现未定义行为,比如 UI 元素无法正确显示、界面闪烁或者程序崩溃等问题。因此,在使用 AsyncTask 时,要清楚了解任务的性质以及对执行线程的要求,确保将任务提交到合适的线程中去执行。

        例如,当需要更新游戏中的 UI 文本显示时,必须通过 AsyncTask 将更新 UI 的任务逻辑提交到游戏线程,示例如下:

void UpdateUIText(const FString& NewText)
{AsyncTask(ENamedThreads::GameThread, [NewText]() {// 获取对应的 UI 控件并更新其文本内容,GetUITextWidget 函数用于获取 UI 文本控件UTextWidget* TextWidget = GetUITextWidget();if (TextWidget!= nullptr){TextWidget->SetText(FText::FromString(NewText));}});
}

(3)任务执行顺序和时间不确定性

        由于 AsyncTask 是将任务放入线程的任务队列中等待执行,并且不同线程的调度顺序以及执行时间受到多种因素影响(如系统负载、线程优先级等),所以无法准确保证提交的异步任务一定会按照调用 AsyncTask 的顺序依次执行,也不能精确预测任务具体的执行时间。

        例如,连续提交了几个异步任务到同一个线程中,可能会因为系统资源分配、其他高优先级任务插入等原因,导致后提交的任务反而先执行,或者任务执行的时间间隔比预期的长很多。

        在一些对任务执行顺序和时间有严格要求的场景下,需要额外设计一些同步机制或者逻辑来确保任务按照期望的顺序和时间执行。比如可以通过信号量、条件变量等机制来协调多个异步任务之间的执行顺序,或者添加一些等待和检查机制来确保某个关键任务完成后再执行后续任务等。

使用示例

        通过 AsyncTask 实现累加求和任务。

1. 新建一个空白c++类“ThreadSubsystem”,继承“UGameInstanceSubsystem”,然后添加三个成员函数“InitAsyncTask”、“GetMyNum”、“SetMyNum”和一个成员变量“MyNum”

2. “InitAsyncTask”实现如下。首先将类成员变量 MyNum 的值复制到局部变量 CopyNum 中,然后在一个异步任务(提交到 ENamedThreads::AnyThread ,也就是任意可用线程)中计算从 0 到 9999 的累加和并累加到 CopyNum 上,接着让线程休眠 0.5 秒,之后再通过另一个异步任务(提交到 ENamedThreads::GameThread ,游戏线程)将计算得到的总和 sum 设置回类的成员变量 MyNum 中,并输出一条包含总和值的日志信息,以此展示了在不同线程中执行任务以及跨线程更新数据的流程。

3. “GetMyNum”、“SetMyNum”实现如下。其中“SetMyNum”函数主要目的是设置 MyNum 的值。不过,在设置值之前做了一个线程判断,即检查当前是否处于游戏线程中。如果当前不是在游戏线程,通过 AsyncTask 将设置值的操作重新提交到游戏线程中去执行,以此来确保对 MyNum 成员变量的赋值操作符合线程安全的要求(通常很多与游戏逻辑相关的变量操作需要在游戏线程中进行,以避免出现未定义行为)。

4. 编译后,在关卡蓝图中设置通过按键触发函数“InitAsyncTask”

调用后可以看到输出的日志信息

“ThreadSubsystem”类 代码:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "SimpleRunnable.h"
#include "HAL/ThreadManager.h"
#include "ThreadSubsystem.generated.h"UCLASS()
class STUDY_API UThreadSubsystem : public UGameInstanceSubsystem
{GENERATED_BODY()public:virtual bool ShouldCreateSubsystem(UObject* Outer) const override;virtual void Initialize(FSubsystemCollectionBase& Collection) override;virtual void Deinitialize() override;public:UFUNCTION(BlueprintCallable)void InitAsyncTask();int32 GetMyNum();void SetMyNum(int32 InInt);void BloackThreadPool();protected:int32 MyNum = 0;};
// Fill out your copyright notice in the Description page of Project Settings.#include "Thread/ThreadSubsystem.h"bool UThreadSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{return true;
}void UThreadSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{Super::Initialize(Collection);
}void UThreadSubsystem::Deinitialize()
{Super::Deinitialize();
}void UThreadSubsystem::InitAsyncTask()
{int32 CopyNum = MyNum;AsyncTask(ENamedThreads::AnyThread, [this, CopyNum]() {int32 sum = CopyNum;for (size_t i = 0; i < 10000; i++){sum += i;}FPlatformProcess::Sleep(0.5);AsyncTask(ENamedThreads::GameThread, [this, sum]() {SetMyNum(sum);UE_LOG(LogTemp, Warning, TEXT("Sum: %d"), sum);});});
}int32 UThreadSubsystem::GetMyNum()
{return MyNum;
}void UThreadSubsystem::SetMyNum(int32 InInt)
{if (!IsInGameThread()){AsyncTask(ENamedThreads::GameThread, [this, InInt]() {SetMyNum(InInt);});}MyNum = InInt;
}

相关文章:

【UE5 C++课程系列笔记】23——多线程基础——AsyncTask

目录 概念 函数说明 注意事项 &#xff08;1&#xff09;线程安全问题 &#xff08;2&#xff09;依赖特定线程执行的任务限制 &#xff08;3&#xff09;任务执行顺序和时间不确定性 使用示例 概念 AsyncTask 允许开发者将一个函数或者一段代码逻辑提交到特定的线程去执…...

基于Python的音乐播放器 毕业设计-附源码73733

摘 要 本项目基于Python开发了一款简单而功能强大的音乐播放器。通过该音乐播放器&#xff0c;用户可以轻松管理自己的音乐库&#xff0c;播放喜爱的音乐&#xff0c;并享受音乐带来的愉悦体验。 首先&#xff0c;我们使用Python语言结合相关库开发了这款音乐播放器。利用Tkin…...

cursor vip

https://cursor.jeter.eu.org?pf7f4f3fab0af4119bece19ff4a4360c3 可以直接复制命令使用git bash执行即可 命令&#xff1a; bash <(curl -Lk https://gitee.com/kingparks/cursor-vip/releases/download/latest/ic.sh) f7f4f3fab0af4119bece19ff4a4360c3 等待执行完成后…...

Docker部署项目,Mysql数据库总是宕机并且上传数据全部被删除了

刚开始排查原因我以为是一些内存占用问题的原因&#xff0c;后来查看数据库日志发现有多个异常ip尝试连接数据库并且也连接成功了随后数据库就被异常关闭了&#xff0c;然后我就重启容器远程连接数据库发现数据全没了&#xff0c;又在数据库中找到了如下内容&#xff1a; All y…...

C++ 复习总结记录六

C 复习总结记录六 模板初阶主要内容 1、泛型编程 2、函数模板 3、类模板 4、STL 简介 一 泛型编程 如何实现一个通用的交换函数 void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right…...

spring boot 集成 knife4j

1、knife4j介绍以及环境介绍 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名knife4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!其底层是对Springfox的封装&#xff0c;使用方式也和Springfox一致&#xff0c;只是对接口…...

WordPress静态缓存插件WP Super Cache与 WP Fastest Cache

引言 WordPress是一款开源的内容管理系统&#xff08;CMS&#xff09;&#xff0c;最初作为博客平台开发&#xff0c;现已发展成为一个功能强大的建站工具&#xff0c;支持创建各种类型的网站&#xff0c;包括企业网站、在线商店、个人博客等。它具有用户友好的界面、丰富的插…...

Pytest钩子函数,测试框架动态切换测试环境

在软件测试中&#xff0c;测试环境的切换是个令人头疼的问题。不同环境的配置不同&#xff0c;如何高效切换测试环境成为许多测试开发人员关注的重点。你是否希望在运行测试用例时&#xff0c;能够动态选择测试环境&#xff0c;而不是繁琐地手动修改配置&#xff1f; Pytest 测…...

VUE3封装一个Hook

在 Vue 3 中&#xff0c;Composition API 让我们能够封装和复用代码逻辑&#xff0c;尤其是通过 setup 函数进行组件间的复用。为了提高代码的可复用性&#xff0c;我们可以把一些常见的 API 请求和状态管理逻辑封装到一个单独的 hook 中。 以下是一个简单的例子&#xff0c;我…...

【Spring Boot】Spring AOP 快速上手指南:开启面向切面编程新旅程

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的入门介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…...

HTML基础入门——简单网页页面

目录 一&#xff0c;网上转账电子账单 ​编辑 1&#xff0c;所利用到的标签 2&#xff0c;代码编写 3&#xff0c;运行结果 二&#xff0c;李白诗词 1&#xff0c;所用到的标签 2&#xff0c;照片的编辑 3&#xff0c;代码编写 4&#xff0c;运行结果 一&#xff0c;网…...

INT301 Bio Computation 题型整理

perceptron 设计和计算 1. XOR: 当两个输入值中只有一个为真时&#xff0c;输出为真 2. 3. 5. 6. 7. 2^3 2^n 9. a) 直接test b) 把v≥2 改成 v≥1 10. no, because it cant be separate through only one decision boundary,its not linearlly separable. Backpropagatio…...

机器学习免费使用的数据集及网站链接

机器学习领域存在许多可以免费使用的数据集&#xff0c;这些数据集来自于学习、研究、比赛等目的。 一、综合性数据集平台 1.Kaggle 网址&#xff1a;Kaggle 数据集https://www.kaggle.com/datasets Kaggle是一个数据科学竞赛和社区平台&#xff0c;提供了大量的数据集供用…...

低空经济——飞行汽车运营建模求解问题思路

1. 掌握问题背景和领域知识 目标&#xff1a; 理解飞行汽车及其运营问题的核心要素和应用背景。学习内容&#xff1a; 飞行汽车基础&#xff1a; 了解飞行汽车的技术特点&#xff08;垂直起降、电动推进等&#xff09;。阅读行业报告&#xff0c;如 Uber Elevate 白皮书。共享…...

英伟达Project Digits赋能医疗大模型:创新应用与未来展望

英伟达Project Digits赋能医疗大模型&#xff1a;创新应用与未来展望 一、引言 1.1 研究背景与意义 在当今数字化时代&#xff0c;医疗行业作为关乎国计民生的关键领域&#xff0c;正面临着前所未有的挑战与机遇。一方面&#xff0c;传统医疗模式在应对海量医疗数据的处理、复…...

【Python3】异步操作 redis

aioredis 在高版本已经不支持了&#xff0c; 不要用 代码示例 redis 连接池异步操作redis以及接口 import asyncio from sanic import Sanic from sanic.response import json import redis.asyncio as redis from redis.asyncio import ConnectionPool# 创建 Sanic 应用 app…...

【W800】UART 的使用与问题

1.开发环境 OS: Windows 11开发板&#xff1a;海凌科 HLK-W800-KIT-PROSDK: W80X_SDK_v1.00.10IDE: CSKY Development Kit 2.UART 使用 在 SDK 中创建文件 uart_test.h 和 uart_test.c&#xff0c;然后在 CDK 项目中添加这两个文件&#xff0c;CDK 会自动 include 头文件。 …...

UART串口数据分析

串口基础知识详细介绍&#xff1a; 该链接详细介绍了串并行、单双工、同异步、连接方式 https://blog.csdn.net/weixin_43386810/article/details/127156063 该文章将介绍串口数据的电平变化、波特率计算、脉宽计算以及数据传输量的计算。 捕获工具&#xff1a;逻辑分析仪&…...

NFS 组件容器化部署实战指南

文章目录 前言部署NFS服务器K8S部署NFS问题记录 前言 使用nfs-client-provisioner这个应用&#xff0c;利用nfs server给kubernets提供作为持久化后端&#xff0c;并且动态提供pv。所有节点需要安装nfs-utils组件&#xff0c;并且nfs服务器与kubernets worker节点都能网络连通…...

嵌入式软件C语言面试常见问题及答案解析(三)

嵌入式软件C语言面试常见问题及答案解析(三) 上一篇已经足够长了,再长也就有点不礼貌了,所以在这儿继续来总结分享那个面试中遇到的题目,文中的问题和提供的答案或者代码均代表个人的理解,如有不合理或者错误的地方,欢迎大家批评指正。 本文中题目列表 1. 编码实现子串定…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...