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

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.xdnf.cn/news/12369.html

相关文章:

  • 杰理可视化SDK--系统死机异常调试
  • vue3 eslint ts 关闭多单词命名检查
  • 第一章:多模态AI导论 —— 感知、理解与交互的智能新纪元
  • thinkphp8.1 调用巨量广告API接口,刷新token
  • 测试W5500的第11步_使用ARP解析IP地址对应的MAC地址
  • 以光量子为例,详解量子获取方式
  • 使用 async/await 封装 wx.request 请求
  • NLP学习路线图(二十五):注意力机制
  • 会计 - 金融负债和权益工具
  • NC | 基于语言模型的药物设计新方法
  • jenkins脚本查看及备份
  • AppTrace技术全景:开发者视角下的工具链与实践经验
  • 人工智能的社交课:从博弈游戏到健康关怀
  • .Net Framework 4/C# LINQ*
  • Python----目标检测(yolov5-7.0安装及训练细胞)
  • 国芯思辰|SCS5501/5502芯片组打破技术壁垒,重构车载视频传输链路,兼容MAX9295A/MAX96717
  • Python爬虫实战:研究RoboBrowser库相关技术
  • [大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
  • 国内头部的UWB企业介绍之品铂科技
  • 关于根据词库分词的算法逻辑实现(最长词汇匹配原则)
  • 6月5日day45
  • 《Pytorch深度学习实践》ch8-多分类
  • 空间利用率提升90%!小程序侧边导航设计与高级交互实现
  • 【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信拓扑与操作 BR/EDR(经典蓝牙)和 BLE
  • 从0开始学习R语言--Day17--Cox回归
  • 深度学习张量
  • Postman接口测试之postman设置接口关联,实现参数化
  • selinux firewalld
  • 将HTML内容转换为Canvas图像,主流方法有效防止文本复制
  • RunnablePassthrough介绍和透传参数实战