[Unity] UniTask 是 Unity 的一个新的 async / await 库[1]

英文原文:https://neuecc.medium.com/unitask-a-new-async-await-library-for-unity-a1ff0766029

我已经在 GitHub 上发布了新库。

GitHub — Cysharp/UniTask

虽然这是一项更新,但可以说并不新鲜,因为它最初是作为 UniRx 的一项功能发布的,现在已成为一个独立的项目。在更新 UniRx 的同时,也对其进行了更新,现在它们已经相互独立,完全没有相互依存的关系。

C#/Unity 的 async/await 异步处理

异步处理一般可以通过完成后回调来调出方法来实现。它也经常在 Unity 中使用,但是

  • 复杂的处理过程产生多个嵌套
  • 在此期间,内部排除不会向外部传送
  • 您无法再从代码中看到处理顺序

这些问题导致了所谓的 “回调地狱”。
取而代之的是,Unity 通过使用 yield return(生成器)完成的 coroutines 来提供异步处理。
Unity 上的这种 coroutine 异步处理的好处是可以按顺序编写,但同时

  • 它在启动时与 MonoBehaviour 结合在一起
  • 传输返回值
  • 传输异常
  • 控制多个coroutines程序(串行/并行处理)

为了相互补充上述不足,我们可以用 coroutines 来编写冗长复杂的进程,通过回调来传输值,并尽量避免嵌套过深。这就是我们迄今为止的处理方法。

您可以使用 UniRx 来降低这种异步处理的复杂性,但是

  • 其功能过于强大,容易造成谜题般的复杂性
  • 仅凭 IObervable<T> 无法区分事件(长度为 ∞)和异步(长度为 1)。
  • 不能按程序编写,使描述不容易理解

这些副作用使它无法成为 “银弹”。

async/await 的设计初衷是将 "像写同步处理一样写异步处理 "作为语言的一项功能,因此可以写出类似下面的矩阵,以区别于 Rx 的用法。

在这里插入图片描述
处理同步的单次返回值只需调用方法即可,而处理异步的单次返回值只需在方法中写入 await 即可。就像使用 foreach 处理同步的多个返回值一样,在 Rx 中进行事件处理可以自然地区分两者的用途。

C# 8.0 中纳入的 AsyncEnumerable 使异步foreach 成为可能。它与 Rx 的不同之处在于,AsyncEnumerable 可以进行 Pull 异步序列处理,而 IObservable 可以进行 Push 异步序列处理。

通过使用 async/await

  • 您可以按程序编写程序,嵌套也会消失
  • 您自然可以处理返回值、异常处理和同步处理

现在您可以以理想的方式进行描述了。但是,任何处理都不可能没有副作用,而 async/await 有以下缺点

  • 调用所有异步进程的方法都变成Task(或类似方法)

UniTask 和 Unity

Unity 已经支持最新版本的 C#,并且可以使用 async/await,但框架方面却不支持,这意味着你无法实际使用它。为了实现在 UniTask 上使用 coroutines 所能实现的功能,我们实施了一些扩展。

// 您可以原样等待 Unity 的异步对象
var asset = await Resources.LoadAsync<TextAsset>("foo");
 
// You can also add progress callbacks with .ConfigureAwait
await SceneManager.LoadSceneAsync("scene2").ConfigureAwait(Progress.Create<float>(x => Debug.Log(x)));
 
// Await frame base; for example you can wait 100 frames
await UniTask.DelayFrame(100); 

// A replacement for WaitForSeconds/WaitForSecondsRealtime
await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
 
// A replacement for yield return WaitForEndOfFrame, yield return null, yield return WaitForFixedUpdate also replaceable with Yield
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate);
 
// Await for IEnumerator coroutines
await FooCoroutineEnumerator();
 
// You can also do things like yield return WaitUntil
await UniTask.WaitUntil(() => isActive == false);

// Multithreading by executing the following processes through thread pool
await UniTask.SwitchToThreadPool();

// Asynchronous Gets for the following kinds of UnityWebRequest
async UniTask<string> GetTextAsync(UnityWebRequest req)
{
    var op = await req.SendWebRequest();
    return op.downloadHandler.text;
}

var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com"));
var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));

// It is simple to write things like parallel execution, then wait. It is also easy to obtain the return values.
var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);
 
// It is also easy to handle timeouts.
await GetTextAsync(UnityWebRequest.Get("http://unity.com")).Timeout(TimeSpan.FromMilliseconds(300));


它的构建方式让你可以在安装 UniTask 后立即同步/等待任何你想要的东西。

UniTask vs Task

您可以在标准 C# 中使用 Task<T> 作为 async/await 的返回值,但为了提高 UniTask 性能与 Unity 的兼容性,我使用了 Unity 专用的、更轻量级的 UniTask<T>,而不是通用性更强的标准 Task。我通过实现 C# 7.0 中添加的 AsyncMethodBuilder 实现了这一点。

  • 不使用 ExecutionContext/SynchronizationContext(可减少开销)
  • 内部同步完成后,对值类型使用零分配
  • 使用类似于 Unity 例程的原始方法组,扩展了 PlayerLoop
  • 利用跟踪窗口的原始扩展防止泄漏

在这里插入图片描述
通过以上几点,与不使用 async/await 的 UniTask 相比,它的优点要多得多。

概要

Unity 2018.3 正式发布后,async/await 支持已成为标准配置,因此您可以不受限制地使用 async/await。不过,可能仍只有少数人在真正使用它。如果 UniTask 能够帮助人们实现它,我会非常高兴。

顺便说一下,Cysharp 是我成立的公司(Cygames 的子公司);我们开发了MagicOnion和 UniTask 等产品,目的是统一 .NET Core 和 Unity。我正在努力构建一个作为 "公司 "的维护系统。

关于更新缓慢的UniRx,我正在考虑通过转移社区基础来转移它,因此也请大家关注。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值