概述(Overview)
UniTaskManager 是一个全局异步任务管理器,用于统一管理 Unity 项目中基于 UniTask 的异步操作及其取消逻辑。
核心设计目标:
- 为每个异步任务分配唯一标识(int ID 或 string key)
- 支持创建、取消、查询任务
- 提供统一取消所有任务的能力(常用于返回主菜单、退出场景、清理状态时)
- 支持 CancellationToken 安全传递,避免异步任务在场景切换或物体销毁后继续运行
- 内置调试日志开关,便于开发时跟踪任务状态
典型使用场景:
- 引导流程、加载动画、计时器、动画序列
- 需要中途取消的长时间异步操作(如网络请求、文件读取、动画等待)
- 场景切换时统一清理所有异步任务
- 防止异步任务在物体销毁后继续执行导致错误
核心数据结构:
- Dictionary<int, CancellationTokenSource>:ID 任务映射
- Dictionary<string, CancellationTokenSource>:字符串 key 任务映射
方法一览(API Index)
| 方法名 | 类型 | 异步 | 返回类型 | 主要功能 | 典型用途 |
|---|---|---|---|---|---|
| CreateIdTask | static | 否 | (int id, CancellationTokenSource cts) | 创建一个带唯一自增 ID 的新任务 | 临时、匿名异步任务 |
| CreateStrTask | static | 否 | (string key, CancellationTokenSource cts) | 创建/替换一个带自定义字符串 key 的任务 | 有语义的任务(如 "LoadScene"、"FadeIn") |
| CancelIdTask | static | 否 | void | 取消并销毁指定 ID 的任务 | 单个任务中断 |
| CancelStrTask | static | 否 | void | 取消并销毁指定 key 的任务 | 按业务名称取消 |
| CancelAllTasks | static | 否 | void | 取消并销毁所有任务(ID + Str) | 场景切换、返回主菜单、清理状态 |
| HasIdTask / HasStrTask | static | 否 | bool | 检查任务是否存在 | 判断任务是否在运行 |
| GetTokenById / GetTokenByStr | static | 否 | CancellationToken | 获取任务的 CancellationToken(不存在返回 None) | 传递给 UniTask 方法 |
| SetVerbose | static | 否 | void | 开启/关闭调试日志 | 开发调试 |
| SetDelay | static | 是 | UniTask | 延时指定秒数(支持取消) | 简洁等待 |
| SetFrame | static | 是 | UniTask | 延时指定帧数(支持取消) | 等待特定帧数 |
| NextFrame | static | 是 | UniTask | 等待下一帧(支持取消) | 确保下一帧执行 |
方法详情
创建任务
CreateIdTask
/// <summary>
/// 创建一个基于自增 ID 的新异步任务,返回 ID 和 CancellationTokenSource
/// ID 从 0 开始递增,适合临时任务
/// </summary>
/// <returns>(任务唯一 ID, 对应的 CancellationTokenSource)</returns>
public static (int id, CancellationTokenSource cts) CreateIdTask()
用法:
var (id, cts) = UniTaskManager.CreateIdTask();
await DoSomethingAsync(cts.Token);
// 完成后可手动取消:UniTaskManager.CancelIdTask(id);
CreateStrTask
/// <summary>
/// 创建一个基于字符串 key 的任务
/// 若已存在同名 key,则先取消并销毁旧任务,再创建新任务
/// </summary>
/// <param name="key">任务的唯一字符串标识(如 "LoadScene"、"FadeUI")</param>
/// <returns>(key, CancellationTokenSource)</returns>
public static (string key, CancellationTokenSource cts) CreateStrTask(string key)
用法:
var (key, cts) = UniTaskManager.CreateStrTask("Loading");
await LoadSceneAsync(cts.Token);
// 随时可取消:UniTaskManager.CancelStrTask("Loading");
取消任务
CancelIdTask / CancelStrTask
/// <summary>
/// 取消并销毁指定 ID 的任务
/// </summary>
public static void CancelIdTask(int id)
/// <summary>
/// 取消并销毁指定 key 的任务
/// </summary>
public static void CancelStrTask(string key)
CancelAllTasks
/// <summary>
/// 取消并销毁当前所有异步任务(ID + Str)
/// 常用于返回主菜单、退出场景、清理状态时调用
/// </summary>
public static void CancelAllTasks()
推荐调用时机:
- OnDestroy / OnDisable(当前物体销毁时)
- 返回主菜单按钮点击
- 场景切换前
查询与令牌获取
HasIdTask / HasStrTask
public static bool HasIdTask(int id)
public static bool HasStrTask(string key)
GetTokenById / GetTokenByStr
/// <summary>
/// 获取指定 ID 任务的 CancellationToken(不存在返回 CancellationToken.None)
/// </summary>
public static CancellationToken GetTokenById(int id)
/// <summary>
/// 获取指定 key 任务的 CancellationToken
/// </summary>
public static CancellationToken GetTokenByStr(string key)
典型用法:
var token = UniTaskManager.GetTokenByStr("Loading");
await UniTask.Delay(5000, cancellationToken: token);
延时辅助方法
SetDelay / SetFrame / NextFrame
/// <summary>
/// 延时指定秒数(支持取消)
/// </summary>
public static async UniTask SetDelay(double time, CancellationToken ctk)
/// <summary>
/// 延时指定帧数
/// </summary>
public static async UniTask SetFrame(int time, CancellationToken ctk)
/// <summary>
/// 等待下一帧
/// </summary>
public static async UniTask NextFrame(CancellationToken ctk)
推荐替代:await UniTask.Delay(TimeSpan.FromSeconds(1), cancellationToken: ctk);
使用建议与最佳实践
-
创建任务
- 临时任务用 CreateIdTask()
- 有业务含义的任务用 CreateStrTask("xxx")(便于阅读和取消)
-
传递 Token 所有异步方法都应接收 CancellationToken:
await DoSomethingAsync(cts.Token); -
取消处理 在异步方法中捕获 OperationCanceledException:
try { await LoadDataAsync(cts.Token); } catch (OperationCanceledException) { Debug.Log("任务被取消"); } -
统一清理 在返回主菜单、退出场景、物体销毁时调用:
UniTaskManager.CancelAllTasks(); -
调试 开启日志查看任务创建/取消:
UniTaskManager.SetVerbose(true); -
注意事项
- CancellationTokenSource 本身不负责停止协程或 UniTask,必须手动传递 Token
- 销毁物体后仍需手动取消对应任务,否则可能出现空引用
- 大量任务时,建议在 CancelAllTasks 后检查是否全部清理干净