Unity入门教程之异步篇第五节:对UniTask的高级封装

Unity入门教程之异步篇第一节:协程基础扫盲–非 Mono 类如何也能启动协程?-CSDN博客

Unity入门教程之异步篇第二节:协程 or UniTask?Unity 中异步流程到底怎么选-CSDN博客

Unity入门教程之异步篇第三节:多线程初探?理解并发与线程安全-CSDN博客

Unity入门教程之异步篇第四节:Unity 高性能计算?Job System 与 Burst Compiler !-CSDN博客

Unity入门教程之异步篇第五节:对UniTask的高级封装-CSDN博客

Unity入门教程之异步篇第六节:对Job System的高级封装

为什么要对 UniTask 进行二次封装?

在开始前,我们首先需要回答这个问题,我们为什么要对UniTask进行二次封装?回答是:UniTask 提供了底层的异步操作原语,就像搭乐高的基础砖块。然而,直接使用这些基础砖块在大型或复杂的项目中可能会遇到一些挑战:

  1. 提高易用性和开发效率:

    • 减少重复代码: 许多异步操作模式在项目中会反复出现,例如:等待一段时间、加载文件、网络请求等。如果每次都从头编写 UniTask.Delayawait UniTask.SwitchToThreadPool() 等,会产生大量重复且相似的代码。
    • 统一接口: UniTask 本身有多种等待方式(按时间、按帧、等待条件),通过封装可以提供一个更统一、更语义化的接口。
    • 隐藏底层细节: 封装可以将线程切换、取消令牌管理、异常处理等底层复杂性隐藏起来,让业务逻辑开发者能更专注于他们要实现的功能,而不是异步编程的细节。
  2. 增强代码可读性和可维护性:

    • 语义化命名: 通过封装,我们可以用更具业务含义的名称来命名异步操作,例如 DownloadAssetAsyncFadeOutUI,而不是泛泛的 DoSomethingAsync
    • 降低心智负担: 当你看到 await MyTaskUtils.WaitFor(2.5f) 时,立刻就知道它是在等待,而不需要去思考 UniTask.Delay 内部参数的含义。
    • 集中修改: 如果未来 UniTask 某个 API 的使用方式发生变化,或者需要统一修改某个异步行为(例如统一的日志输出),只需要修改封装层,而不需要修改所有使用到该 API 的地方。
  3. 提升代码健壮性和安全性:

    • 统一异常处理: 异步操作中的异常处理往往容易被忽略。封装层可以内置 try-catch 块,确保所有异步任务的异常都能被捕获和处理,避免程序崩溃。
    • 自动取消管理: CancellationTokenUniTask 的核心,但手动管理 CancellationTokenSource 的生命周期容易出错。封装可以自动绑定任务与 MonoBehaviour 的生命周期,防止内存泄漏和僵尸任务。
    • 线程安全: 封装可以确保敏感操作(如文件 IO)在正确的线程(后台线程)上执行,并在必要时安全地切回主线程,避免多线程访问 Unity API 的错误。

如何对 UniTask 进行二次封装(代码示例与设计意图)

我们将通过四个常用场景来展示如何进行二次封装,涵盖了延迟等待、文件 IO、周期性任务和生命周期绑定。


1. 统一的延迟等待方法:MyTaskUtils.WaitFor

代码:

using Cysharp.Threading.Tasks;
using System.Threading;
using System;
using UnityEngine;

public static class MyTaskUtils
{
   
   
    /// <summary>
    /// 异步等待指定的时间(秒)或帧数。
    /// 默认在主线程更新。
    /// </summary>
    /// <param name="delayTime">等待时间(秒)。如果小于等于 0,则等待指定帧数。</param>
    /// <param name="delayFrames">如果 delayTime <= 0,则等待此帧数。</param>
    /// <param name="ignoreTimeScale">是否忽略 Time.timeScale 的影响。</param>
    /// <param name="cancellationToken">可选的取消令牌。</param>
    /// <returns>UniTask。</returns>
    public static async UniTask WaitFor(
        float delayTime,
        int delayFrames = 1,
        bool ignoreTimeScale = false,
        CancellationToken cancellationToken = default)
    {
   
   
        if (delayTime > 0)
        {
   
   
            await UniTask.Delay(TimeSpan.FromSeconds(delayTime), ignoreTimeScale, PlayerLoopTiming.Update, cancellationToken);
        }
        else
        {
   
   
            await UniTask.DelayFrame(delayFrames, PlayerLoopTiming.Update, cancellationToken);
        }
    }
}

设计意图和写法:

  • 单一职责: 这个方法旨在提供一个统一的“等待”功能。在 Unity 中,我们常常需要按时间等待(受 Time.timeScale 影响或不影响),或者按帧等待。UniTask 提供了 DelayDelayFrame 两个独立的方法来处理。
  • 参数合并与智能判断: 我将 delayTimedelayFrames 合并到一个方法签名中。通过判断 delayTime 是否大于 0,智能地选择调用 UniTask.DelayUniTask.DelayFrame,简化了调用方的心智负担。
  • 默认参数: delayFrames 默认为 1,ignoreTimeScale 默认为 falsecancellationToken 默认为 default。这使得最常见的“等待一小段时间”或“等待一帧”的调用变得非常简洁,例如 await WaitFor(2.5f);await WaitFor(0);
  • PlayerLoopTiming.Update 明确指定了在 Update 阶段进行等待,这是最常用的游戏逻辑更新时机。
  • 可取消性: 依然保留了 cancellationToken 参数,确保封装后的方法仍然支持任务取消,这是 UniTask 的核心优势之一。

2. 安全的文件 IO 操作:FileIOTaskUtils

代码:

using Cysharp.Threading.Tasks;
using System.IO;
using System.Text;
using System.Threading;
using 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉良吉影NeKoSuKi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值