UPL5-4——协程相关

协同程序的额外开销

上一章的 Update​ 优化策略当中提到,我们可以通过降低 Update 的执行频率以及分帧执行来提升效率
很容易联想到使用协程来实现这一目的,但是我们当时并没有提及协程

主要原因就是,使用协程其实会带来一些额外开销,与标准函数调用相比:

  1. 启动协程会带来额外的开销成本,大约是标准函数调用的 3 倍
  2. 启动协程会额外分配一些内存,用于存储当前协程的状态
  3. 由于协程中会使用 yield​,那么每次执行 yield 逻辑,都会一次次的产生相同的开销成本

因此我们在使用协程时,要考虑这些成本的消耗 是否小于 它带来的好处才使用它

利用 Update 替代协程处理时间

某些基于时间的操作(如倒计时)也可以在 Update​ 中配合 Time.deltaTime 实现,无需协程

避免协程中逻辑过于复杂

如果协程嵌套调用、状态跳转多时,维护成本会更高,会让问题难以追踪,调试困难
因此我们要尽量避免这种情况的发生

避免大量并发的协程

如果场景中大量对象同时启动协程,会造成大量的调度开销和 GC 压力,我们应该避免这种情况的发生
比如:对象中存在自带的定时执行逻辑
我们可以利用上节课学习的 Update 相关知识解决(降低执行频率、自定义 Update 管理器等)

善用 WaitForSecondsRealtime

WaitForSecondsRealtime​ 相比 WaitForSeconds 前者不受到时间缩放影响,而后者会,我们需要根据自己的需求选择性使用

利用 C# 的 async / await 相关方案替代协程

async​ / await​ 的性能消耗比起协程更好,但是 Unity 自带的一些异步加载本身并不支持配合它们使用
异步方法的使用详见:CS5L7——CSharp 5 功能和语法 中的 async 部分

因此我们可以利用第三方工具

他们让 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;
}