UPL5-4——协程相关
UPL5-4——协程相关
协同程序的额外开销
上一章的 Update 优化策略当中提到,我们可以通过降低 Update 的执行频率以及分帧执行来提升效率
很容易联想到使用协程来实现这一目的,但是我们当时并没有提及协程
主要原因就是,使用协程其实会带来一些额外开销,与标准函数调用相比:
- 启动协程会带来额外的开销成本,大约是标准函数调用的 3 倍
- 启动协程会额外分配一些内存,用于存储当前协程的状态
- 由于协程中会使用
yield,那么每次执行yield逻辑,都会一次次的产生相同的开销成本
因此我们在使用协程时,要考虑这些成本的消耗 是否小于 它带来的好处才使用它
利用 Update 替代协程处理时间
某些基于时间的操作(如倒计时)也可以在 Update 中配合 Time.deltaTime 实现,无需协程
避免协程中逻辑过于复杂
如果协程嵌套调用、状态跳转多时,维护成本会更高,会让问题难以追踪,调试困难
因此我们要尽量避免这种情况的发生
避免大量并发的协程
如果场景中大量对象同时启动协程,会造成大量的调度开销和 GC 压力,我们应该避免这种情况的发生
比如:对象中存在自带的定时执行逻辑
我们可以利用上节课学习的 Update 相关知识解决(降低执行频率、自定义 Update 管理器等)
善用 WaitForSecondsRealtime
WaitForSecondsRealtime 相比 WaitForSeconds 前者不受到时间缩放影响,而后者会,我们需要根据自己的需求选择性使用
利用 C# 的 async / await 相关方案替代协程
async / await 的性能消耗比起协程更好,但是 Unity 自带的一些异步加载本身并不支持配合它们使用
异步方法的使用详见:CS5L7——CSharp 5 功能和语法 中的 async 部分
因此我们可以利用第三方工具
-
UniTask(推荐):https://github.com/Cysharp/UniTask -
Unity3dAsyncAwaitUtil(过于久远,不建议使用):https://github.com/ModestTree/Unity3dAsyncAwaitUtil
他们让 Unity 中本来可以配合协程使用的API,也支持配合 async / await 使用
在 Unity 6 及以后的版本,Unity 提供了
Awaitable 静态类,它可以作为async 方法的返回值,可以配合await 使用,
大部分 Unity 异步方法借此也可以配合配合async /await 使用了
使用方法可参考:Unity - Manual: Introduction to asynchronous programming with Awaitable
Awaitable静态类配合静态方法,一定程度上可以替代协程,例如:
- 使用
await Awaitable.NextFrameAsync() 代替yield return null- 使用
await Awaitable.WaitForSecondsAsync() 代替yield return WaitForSeconds()- 返回值继承自
AsyncOperation 的方法可以await
1
2
3
4
5
6
7
8
9
10
11
12
13 private async void TestAsync()
{
await Awaitable.NextFrameAsync();
await Resources.LoadAsync("GameObject");
await Awaitable.WaitForSecondsAsync(1f);
}
private IEnumerator TestCoroutine()
{
yield return null;
yield return Resources.LoadAsync("GameObject");
yield return new WaitForSeconds(1f);
}和
Task<> 类似,Awaitable<> 可以让async方法返回值
1
2
3
4
5
6 private async Awaitable<GameObject> TestAsync()
{
var operation = Resources.LoadAsync<GameObject>("GameObject");
await operation;
return operation.asset as GameObject;
}
