Unity协程Coroutine与UniTask对比

原理对比

CoroutineUniTask
本质IEnumerator 的协作调度器async/await 状态机(IAsyncStateMachine)
调度方式Unity 内部调用 MoveNext()自建 PlayerLoopRunner 控制状态推进
内存管理引用类型,频繁分配 GC结构体 UniTask,低 GC 压力
多线程支持主线程限制可结合多线程(但默认仍在主线程)
工具组合能力强(如 WhenAll, WithCancellation)

调用方式

协程

1.不使用StartCoroutine调用时,可通过编译,但是无法启动,协程进不去

2.使用StartCoroutine可正确执行协程逻辑,正常执行等待,对当前函数体无要求

3.可使用yield return等待协程执行完毕,需要当前函数体有IEnumerator关键字标识

4.可使用await关键字等待协程执行完毕,需要当前函数体有async关键字标识

示例代码如下

void CorTest()
{Test();Debug.Log("Coroutine 3");StartCoroutine(Test());Debug.Log("Coroutine 4");
}IEnumerator Test()
{Debug.Log("Coroutine 1");yield return new WaitForSeconds(1.1f);Debug.Log("Coroutine 2");
}

当调用CorTest后,输出结果为

可以看到,先输出了"Coroutine 3",而没有输出"Coroutine 1",表示没有使用StartCoroutine启动时,协程是进不去的。使用了StartCoroutine后,"Coroutine 2"在"Coroutine 1"及"Coroutine 4"一秒后输出。await关键字情况同下面UniTask调用

UniTask

1.UniTask无论是否使用await关键字,都可以正确进入逻辑,正常执行等待,对当前函数体无要求

2.可使用await等待UniTask执行完毕,需要当前函数体有async关键字标识

示例代码如下

async void TaskTest()
{Test2();Debug.Log("UniTask 3");await Test2();Debug.Log("UniTask 4");
}async UniTask Test2()
{Debug.Log("UniTask 1");await UniTask.Delay(1100);Debug.Log("UniTask 2");
}

当调用TaskTest后,输出结果为

可以看到,两次调用Test2均正常进入,且正常执行了等待逻辑,两次"UniTask 2"输出均在"UniTask 1"后,而"UniTask 4"输出也是在"UniTask 2"后执行的。函数体如果没有async关键字时,内部是无法使用await的,编译不通过。

性能测试

yield return null  VS UniTask.Yield()

测试代码

public int times = 1000;void CorProTest()
{StartCoroutine(CorProEnum());
}IEnumerator CorProEnum()
{for (int i = 0; i < times; i++){yield return null;}
}void UniTaskProTest()
{UniTaskProTask();
}async UniTask UniTaskProTask()
{for (int i = 0; i < times; i++){await UniTask.Yield();}
}

由于无法抓取一段时间内的纯Profiler数据,所以只取一帧的数据,每帧数据都是一致的。

可以看到,两个对GC都没有影响,因为协程本身并没有新建对象,所以不存在分配内存。可以理解成等价的。

 yield return new WaitForSeconds VS UniTask.Delay

测试代码

public int times = 1000;void CorProTest()
{StartCoroutine(CorProEnum());
}IEnumerator CorProEnum()
{for (int i = 0; i < times; i++){yield return new WaitForSeconds(0.01f);}
}void UniTaskProTest()
{UniTaskProTask();
}async UniTask UniTaskProTask()
{for (int i = 0; i < times; i++){await UniTask.Delay(10);}
}

同上,抓取某一帧的数据

 可以看到,调用yield return new WaitForSeconds(0.01f)时,有20B的内存分配,这是因为创建了引用对象WaitForSeconds,所以必定会有内存分配。调用await UniTask.Delay(10)没有内存分配,是因为UniTask内部使用的是结构体,而不是类。

yield return new WaitUntil VS UniTask.WaitUntil

测试代码

public int times = 1000;void CorProTest()
{StartCoroutine(CorProEnum());
}IEnumerator CorProEnum()
{bool value = true;for (int i = 0; i < times; i++){yield return new WaitUntil(() => value);}
}void UniTaskProTest()
{UniTaskProTask();
}async UniTask UniTaskProTask()
{bool value = true;for (int i = 0; i < times; i++){await UniTask.WaitUntil(() => value);}
}

同上,抓取某一帧数据

可以看到 ,调用yield return new WaitUntil时,有24B的内存分配,这是因为创建了引用对象WaitUntil,所以必定会有内存分配。调用await UniTask.WaitUntil没有内存分配,是因为UniTask内部使用的是结构体,而不是类。

总结

        测试了这三种常用的用法,可以看到,协程除了null没有GC产生(因为没有创建对象)外,其他两种用户均产生了GC,只是量比较小,而UniTask三种用法都没有GC产生。

        如果只考虑GC方面的差异,在项目使用过程中,如果量比较大,使用比较频繁,建议使用UniTask。而对于一般用量来讲,差距可以忽略不计。而GC是可以使用对象池来优化的,可以一定程度上降低GC的分配。对象池参考另一篇博客从CPU缓存出发对引用池进行优化。

        然而,最终选择使用哪一种,需要结合其他情况考虑,协程使用起来比较方便,而UniTask也有一些比较好的功能,比如UniTask支持带返回值的异步,封装了多任务同时进行、等待以及其他功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/85880.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MAC软件打开提示已损坏:“已损坏,打不开。您应将它移到废纸篓“

打开「终端.app」&#xff0c;输入以下命令并回车&#xff0c;输入开机密码回车 sudo spctl --master-disable 按照上述步骤操作完成后&#xff0c;打开「系统偏好设置」-「安全与隐私」-「通用」&#xff0c;确保已经修改为「任何来源」。 打开「终端.app」&#xff0c;输入…

JAVA之 Lambda

Java Lambda Lambda 表达式是 Java 8 的核心特性&#xff0c;通过 函数式编程 大幅简化代码。其核心思想是将行为作为参数传递&#xff0c;替代匿名内部类&#xff0c;提升代码的简洁性和可读性。以下是系统解析和完整代码示例&#xff1a; 一、Lambda 表达式基础 语法结构 (…

Starrocks中RoaringBitmap杂谈

背景 最近在阅读Starrocks源码的时候&#xff0c;遇到ColumnRefSet的RoaringBitmap使用&#xff0c;所以借此来讨论一下RoaringBitmap这个数据结构,这种思想是很值得借鉴的。 对于的实现可以参考一下 <dependency><groupId>org.roaringbitmap</groupId><…

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 &#x1f50d; 若用递归计算每一项&#xff0c;会发生什么&#xff1f; Horners Rule&#xff08;霍纳法则&#xff09; 第一步&#xff1a;我们从最原始的泰勒公式出发 第二步&#xff1a;从形式上重新观察展开式 &#x1f31f; 第三步&#xff1a;引出霍纳法则&…

从Java的Jvm的角度解释一下为什么String不可变?

从Java的Jvm的角度解释一下为什么String不可变&#xff1f; 从 JVM 的角度看&#xff0c;Java 中 String 的不可变性是由多层次的机制共同保障的&#xff0c;这些设计涉及内存管理、性能优化和安全保障&#xff1a; 1. JVM 内存模型与字符串常量池 字符串常量池&#xff08;St…

初识硬编码(x86指令描述)

硬编码 任何一个程序其实都可以看做两部分组成的&#xff0c;指令和数据 cpu并没有明确的规定哪些要当做数据&#xff0c;哪些要当做指令来执行&#xff0c;把数据给EIP只要是遵循了指定的格式&#xff08;x86 x64 ARM&#xff09;&#xff0c;cpu都会当做指令来执行 x86/x64…

3.RV1126-OPENCV 图像叠加

一.功能介绍 图像叠加&#xff1a;就是在一张图片上放上自己想要的图片&#xff0c;如LOGO&#xff0c;时间等。有点像之前提到的OSD原理一样。例如&#xff1a;下图一张图片&#xff0c;在左上角增加其他图片。 二.OPENCV中图像叠加常用的API 1. copyTo方法进行图像叠加 原理…

MySQL垂直分库(基于MyCat)

参考资料&#xff1a; 参考视频 参考博客 Mycat基本部署 视频参考资料&#xff1a;链接: https://pan.baidu.com/s/1xT_WokN_xlRv0h06b6F3yg 提取码: aag3 概要&#xff1a; 本文的垂直分库&#xff0c;全部是基于前文部署的基本架构进行的 垂直分库&#xff1a; 垂直分库…

Spitfire:Codigger 生态中的高性能、安全、分布式浏览器

Spitfire 是 Codigger 生态系统中的一款现代化浏览器&#xff0c;专为追求高效、隐私和分布式技术的用户设计。它结合了 Codigger 的分布式架构优势&#xff0c;在速度、安全性和开发者支持方面提供了独特的解决方案&#xff0c;同时确保用户对数据的完全控制。 1. 高性能浏览…

1-【源码剖析】kafka核心概念

从今天开始开始在csdn上记录学习的笔记&#xff0c;主要包括以下几个方面&#xff1a; kafkaflinkdoris 本系列笔记主要记录Kafka学习相关的内容。在进行kafka源码学习之前&#xff0c;先介绍一下Kafka的核心概念。 消息 消息是kafka中最基本的数据单元&#xff0c;由key和…

互联网大厂Java求职面试:云原生架构下的微服务网关与可观测性设计

互联网大厂Java求职面试&#xff1a;云原生架构下的微服务网关与可观测性设计 郑薪苦怀着忐忑的心情走进了会议室&#xff0c;对面坐着的是某大厂的技术总监张总&#xff0c;一位在云原生领域有着深厚积累的专家。 第一轮面试&#xff1a;微服务网关的设计挑战 张总&#xf…

【HarmonyOS 5】针对 Harmony-Cordova 性能优化,涵盖原生插件开发、线程管理和资源加载等关键场景

1. ‌原生图片处理插件&#xff08;Java&#xff09; package com.example.plugin; import ohos.media.image.ImageSource; import ohos.media.image.PixelMap; import ohos.app.Context; public class ImageProcessor { private final Context context; public ImagePro…

Java-IO流之缓冲流详解

Java-IO流之缓冲流详解 一、缓冲流概述1.1 什么是缓冲流1.2 缓冲流的工作原理1.3 缓冲流的优势 二、字节缓冲流详解2.1 BufferedInputStream2.1.1 构造函数2.1.2 核心方法2.1.3 使用示例 2.2 BufferedOutputStream2.2.1 构造函数2.2.2 核心方法2.2.3 使用示例 三、字符缓冲流详…

健康检查:在 .NET 微服务模板中优雅配置 Health Checks

&#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks &#x1f4da; 目录 &#x1f680; 健康检查&#xff1a;在 .NET 微服务模板中优雅配置 Health Checks一、背景与意义 &#x1f50d;二、核心配置 &#x1f527;2.1 引入必要的 NuGet 依赖 &…

关于akka官方quickstart示例程序(scala)的记录

参考资料 https://doc.akka.io/libraries/akka-core/current/typed/actors.html#first-example 关于scala语法的注意事项 extends App是个语法糖&#xff0c;等同于直接在伴生对象中编写main 方法对象是通过apply方法创建的&#xff0c;也可以通过对象的名称单独创建&#x…

基于vue3-elemenyui的页面加载及新建浏览页案例

1.参考链接&#xff1a; 基于创建vue3链接&#xff1a;Vue3前端项目创建_vscode创建vue3项目-CSDN博客 基于创建elementui链接&#xff1a;Vue3引入ElementPlus_vue引入element-plus-CSDN博客 2.案例内容 该案例实现了基本的app.vue的路由跳转、新建浏览页参数传入以及浏览…

板凳-------Mysql cookbook学习 (十)

5.6 改变字符串的字符集或字符排序 mysql> set s1 my string; Query OK, 0 rows affected (0.01 sec)mysql> set s2 convert(s1 using utf8); Query OK, 0 rows affected, 1 warning (0.00 sec)mysql> select charset(s1), charset(s2); -------------------------…

使用nginx配置反向代理,负载均衡

首先啥叫反向代理 咋配置呢&#xff0c;那当然是在nginx目录下改conf文件了 具体咋改呢&#xff0c;那就新增一个新的server配置&#xff0c;然后在location里新增你想代理的服务器 实际上负载均衡也就是根据反向代理的思路来的&#xff0c;如下所示 配置的话实际上也与上…

嵌入式开发之STM32学习笔记day20

STM32F103C8T6 PWR电源控制 1 PWR简介 PWR&#xff08;Power Control&#xff09;电源控制单元是STM32微控制器中一个重要的组成部分&#xff0c;它负责管理系统的电源管理功能&#xff0c;以优化功耗并提高效率。PWR负责管理STM32内部的电源供电部分&#xff0c;可以实现可编…

Spring AI(10)——STUDIO传输的MCP服务端

Spring AI MCP&#xff08;模型上下文协议&#xff09;服务器Starters提供了在 Spring Boot 应用程序中设置 MCP 服务器的自动配置。它支持将 MCP 服务器功能与 Spring Boot 的自动配置系统无缝集成。 本文主要演示支持STDIO传输的MCP服务器 仅支持STDIO传输的MCP服务器 导入j…