diff --git a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerAssetListViewer.cs b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerAssetListViewer.cs
index 30e4b8f9..51ba7146 100644
--- a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerAssetListViewer.cs
+++ b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerAssetListViewer.cs
@@ -206,7 +206,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var providerTableData = data as ProviderTableData;
- if (providerTableData.ProviderInfo.Status == EOperationStatus.Failed.ToString())
+ if (providerTableData.ProviderInfo.Status == EOperationStatus.Failed.ToString() ||
+ providerTableData.ProviderInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
@@ -280,7 +281,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var dependTableData = data as DependTableData;
- if (dependTableData.BundleInfo.Status == EOperationStatus.Failed.ToString())
+ if (dependTableData.BundleInfo.Status == EOperationStatus.Failed.ToString() ||
+ dependTableData.BundleInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
diff --git a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerBundleListViewer.cs b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerBundleListViewer.cs
index fc018306..0a96a0b9 100644
--- a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerBundleListViewer.cs
+++ b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerBundleListViewer.cs
@@ -151,7 +151,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var bundleTableData = data as BundleTableData;
- if (bundleTableData.BundleInfo.Status == EOperationStatus.Failed.ToString())
+ if (bundleTableData.BundleInfo.Status == EOperationStatus.Failed.ToString() ||
+ bundleTableData.BundleInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
@@ -267,7 +268,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var usingTableData = data as UsingTableData;
- if (usingTableData.ProviderInfo.Status == EOperationStatus.Failed.ToString())
+ if (usingTableData.ProviderInfo.Status == EOperationStatus.Failed.ToString() ||
+ usingTableData.ProviderInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
@@ -341,7 +343,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var feferenceTableData = data as ReferenceTableData;
- if (feferenceTableData.BundleInfo.Status == EOperationStatus.Failed.ToString())
+ if (feferenceTableData.BundleInfo.Status == EOperationStatus.Failed.ToString() ||
+ feferenceTableData.BundleInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
diff --git a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerOperationListViewer.cs b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerOperationListViewer.cs
index 68095f54..9af278ce 100644
--- a/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerOperationListViewer.cs
+++ b/Assets/YooAsset/Editor/AssetBundleDebugger/VisualViewers/DebuggerOperationListViewer.cs
@@ -207,7 +207,8 @@ namespace YooAsset.Editor
{
StyleColor textColor;
var operationTableData = data as OperationTableData;
- if (operationTableData.OperationInfo.Status == EOperationStatus.Failed.ToString())
+ if (operationTableData.OperationInfo.Status == EOperationStatus.Failed.ToString() ||
+ operationTableData.OperationInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
@@ -479,7 +480,8 @@ namespace YooAsset.Editor
// Status
{
StyleColor textColor;
- if (operationInfo.Status == EOperationStatus.Failed.ToString())
+ if (operationInfo.Status == EOperationStatus.Failed.ToString() ||
+ operationInfo.Status == EOperationStatus.Aborted.ToString())
textColor = new StyleColor(Color.yellow);
else
textColor = new StyleColor(Color.white);
diff --git a/Assets/YooAsset/Runtime/OperationSystem/AsyncOperationBase.cs b/Assets/YooAsset/Runtime/OperationSystem/AsyncOperationBase.cs
index a24acce4..f0fd58b4 100644
--- a/Assets/YooAsset/Runtime/OperationSystem/AsyncOperationBase.cs
+++ b/Assets/YooAsset/Runtime/OperationSystem/AsyncOperationBase.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
@@ -78,13 +78,13 @@ namespace YooAsset
public float Progress { get; protected set; }
///
- /// 任务逻辑是否完成(Status为Succeed或Failed)
+ /// 任务逻辑是否完成(Status为Succeed、Failed或Aborted)
///
public bool IsDone
{
get
{
- return Status == EOperationStatus.Failed || Status == EOperationStatus.Succeed;
+ return Status == EOperationStatus.Succeed || Status == EOperationStatus.Failed || Status == EOperationStatus.Aborted;
}
}
@@ -284,7 +284,7 @@ namespace YooAsset
}
///
- /// 终止异步任务
+ /// 终止异步任务(递归中止所有子任务)
///
internal void AbortOperation()
{
@@ -299,7 +299,7 @@ namespace YooAsset
if (IsDone == false)
{
InternalAbort();
- Status = EOperationStatus.Failed;
+ Status = EOperationStatus.Aborted;
Error = "user abort";
YooLogger.Warning($"Async operation {this.GetType().Name} has been aborted.");
}
@@ -403,7 +403,7 @@ namespace YooAsset
}
///
- /// 等待异步执行完毕
+ /// 同步等待异步执行完毕(会阻塞当前线程)
///
public void WaitForAsyncComplete()
{
diff --git a/Assets/YooAsset/Runtime/OperationSystem/EOperationStatus.cs b/Assets/YooAsset/Runtime/OperationSystem/EOperationStatus.cs
index 10627606..20c1b62e 100644
--- a/Assets/YooAsset/Runtime/OperationSystem/EOperationStatus.cs
+++ b/Assets/YooAsset/Runtime/OperationSystem/EOperationStatus.cs
@@ -27,7 +27,7 @@ namespace YooAsset
Failed,
///
- /// 已中止
+ /// 已中止(用户主动取消)
///
Aborted,
}
diff --git a/Assets/YooAsset/Runtime/OperationSystem/OperationSystem.cs b/Assets/YooAsset/Runtime/OperationSystem/OperationSystem.cs
index 6c979500..f6252fc2 100644
--- a/Assets/YooAsset/Runtime/OperationSystem/OperationSystem.cs
+++ b/Assets/YooAsset/Runtime/OperationSystem/OperationSystem.cs
@@ -190,7 +190,7 @@ namespace YooAsset
}
///
- /// 销毁包裹的所有任务
+ /// 清空并中止包裹的所有任务
///
public static void ClearPackageOperations(string packageName)
{
diff --git a/Assets/YooAsset/Runtime/OperationSystem/README.md b/Assets/YooAsset/Runtime/OperationSystem/README.md
new file mode 100644
index 00000000..eaf74a8f
--- /dev/null
+++ b/Assets/YooAsset/Runtime/OperationSystem/README.md
@@ -0,0 +1,442 @@
+# OperationSystem 异步操作模块
+
+## 模块概述
+
+OperationSystem 是 YooAsset 资源管理系统的**异步操作调度层**,负责管理和调度所有异步操作的生命周期。该模块提供了统一的异步操作抽象,支持协程、Task(async/await)、回调等多种异步编程模式,以及基于优先级的时间切片调度机制。
+
+### 可见性说明
+
+OperationSystem 属于 YooAsset Runtime 的内部基础模块,目录内 `OperationSystem`、`OperationScheduler` 等类型为 `internal`(仅供 YooAsset Runtime 内部程序集使用)。`AsyncOperationBase` 和 `EOperationStatus` 为 `public`,供上层和用户代码使用。
+
+### 核心职责
+
+- 异步操作生命周期管理(启动、更新、完成、中止)
+- 基于优先级的调度排序
+- 时间切片执行(防止主线程阻塞)
+- 多包裹独立调度
+- 父子任务依赖管理
+
+---
+
+## 边界与上层协作
+
+OperationSystem 的职责是提供"统一异步操作抽象 + 优先级调度 + 时间切片执行"的基础能力:
+
+- **本模块不负责具体业务逻辑**:具体的资源加载、下载等逻辑由子类实现
+- **本模块不负责错误重试**:失败后的重试策略由上层或子类实现
+- **本模块不负责资源释放**:资源的引用计数和卸载由 ResourceManager 管理
+
+---
+
+## 设计目标
+
+| 目标 | 说明 |
+|------|------|
+| **统一抽象** | 所有异步操作继承 AsyncOperationBase,提供一致的 API |
+| **多模式支持** | 支持协程(yield return)、Task(async/await)、回调三种异步模式 |
+| **时间切片** | 可配置每帧最大执行时间,防止主线程卡顿 |
+| **优先级调度** | 支持任务和调度器双层优先级,高优先级任务优先执行 |
+| **多包裹隔离** | 每个资源包裹拥有独立调度器,互不干扰 |
+
+---
+
+## 架构概念
+
+### 分层架构
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ 上层调用者 │
+│ (ResourcePackage / ResourceManager) │
+└─────────────────────────┬───────────────────────────────┘
+ │
+┌─────────────────────────▼───────────────────────────────┐
+│ OperationSystem │
+│ (静态调度系统) │
+│ 管理所有调度器,提供时间切片机制 │
+└─────────────────────────┬───────────────────────────────┘
+ │
+┌─────────────────────────▼───────────────────────────────┐
+│ OperationScheduler │
+│ (包裹调度器) │
+│ 管理单个包裹的异步操作队列 │
+└─────────────────────────┬───────────────────────────────┘
+ │
+┌─────────────────────────▼───────────────────────────────┐
+│ AsyncOperationBase │
+│ (异步操作基类) │
+│ 定义操作生命周期和状态机 │
+└─────────────────────────────────────────────────────────┘
+```
+
+### 核心组件
+
+- **OperationSystem**: 静态调度系统,管理所有包裹调度器,提供时间切片执行机制
+- **OperationScheduler**: 包裹调度器,管理单个包裹的异步操作队列和优先级排序
+- **AsyncOperationBase**: 异步操作抽象基类,定义操作生命周期和多种异步编程接口
+- **EOperationStatus**: 操作状态枚举,表示操作的当前状态
+
+---
+
+## 文件结构
+
+```
+OperationSystem/
+├── AsyncOperationBase.cs # 异步操作抽象基类
+├── EOperationStatus.cs # 操作状态枚举
+├── OperationScheduler.cs # 包裹调度器
+├── OperationSystem.cs # 静态调度系统
+└── README.md # 本文档
+```
+
+---
+
+## 接口说明
+
+### EOperationStatus(操作状态枚举)
+
+定义异步操作的生命周期状态。
+
+```csharp
+public enum EOperationStatus
+{
+ None, // 未开始
+ Processing, // 处理中
+ Succeed, // 已成功
+ Failed, // 已失败
+ Aborted // 已中止(用户主动取消)
+}
+```
+
+### AsyncOperationBase(异步操作基类)
+
+所有异步操作的抽象基类,实现 `IEnumerator`(协程支持)和 `IComparable`(优先级排序)。
+
+```csharp
+public abstract class AsyncOperationBase : IEnumerator, IComparable
+{
+ // 状态属性
+ public EOperationStatus Status { get; } // 当前状态
+ public bool IsDone { get; } // 是否完成(Succeed/Failed/Aborted)
+ public string Error { get; } // 错误信息
+ public float Progress { get; } // 进度(0-1)
+ public uint Priority { get; set; } // 优先级(值越大越优先)
+
+ // 异步编程支持
+ public Task Task { get; } // 用于 async/await
+ public event Action Completed; // 完成回调
+
+ // 同步等待
+ public void WaitForAsyncComplete(); // 同步等待完成(阻塞当前线程)
+
+ // 调试信息(仅 DEBUG 模式有效)
+ public string StartTime { get; } // 开始时间(HH:MM:SS)
+ public long ElapsedMS { get; } // 耗时(毫秒)
+}
+```
+
+### 子类必须实现的方法
+
+```csharp
+// 启动时调用(必须实现)
+internal abstract void InternalStart();
+
+// 每帧更新(必须实现)
+internal abstract void InternalUpdate();
+
+// 中止时调用(可选实现)
+internal virtual void InternalAbort() { }
+
+// 同步等待实现(可选实现,不实现则抛出异常)
+internal virtual void InternalWaitForAsyncComplete();
+
+// 获取操作描述(可选实现,用于调试)
+internal virtual string InternalGetDescription();
+```
+
+---
+
+## 核心类说明
+
+### OperationSystem
+
+静态调度系统,管理所有包裹的调度器,提供全局时间切片机制。
+
+**主要功能:**
+- 管理多个包裹调度器的生命周期
+- 提供全局时间切片预算控制
+- 按优先级更新各调度器
+
+```csharp
+internal static class OperationSystem
+{
+ // 时间切片配置
+ public static long MaxTimeSlice { get; set; } // 每帧最大执行时间(毫秒)
+ public static bool IsBusy { get; } // 当前帧时间切片是否已用完
+
+ // 生命周期
+ public static void Initialize(); // 初始化系统
+ public static void Update(); // 每帧更新
+ public static void DestroyAll(); // 销毁系统
+
+ // 调度器管理
+ public static OperationScheduler CreatePackageScheduler(string packageName, uint priority);
+ public static void DestroyPackageScheduler(string packageName);
+ public static void ClearPackageOperations(string packageName);
+
+ // 任务管理
+ public static void StartOperation(string packageName, AsyncOperationBase operation);
+}
+```
+
+### OperationScheduler
+
+包裹调度器,管理单个包裹的异步操作队列。
+
+**主要功能:**
+- 管理操作队列(待处理队列 + 执行队列)
+- 按优先级排序操作
+- 更新和完成操作
+
+```csharp
+internal class OperationScheduler : IComparable
+{
+ public string PackageName { get; } // 所属包裹名称
+ public uint Priority { get; set; } // 调度器优先级
+ public int CreationOrder { get; } // 创建顺序(同优先级稳定排序)
+
+ public void StartOperation(AsyncOperationBase operation); // 启动操作
+ public void Update(); // 更新调度
+ public void ClearAll(); // 清空并中止所有任务
+}
+```
+
+---
+
+## 使用示例
+
+### 协程方式
+
+```csharp
+IEnumerator LoadAssetCoroutine()
+{
+ var operation = package.LoadAssetAsync("Assets/Prefab.prefab");
+ yield return operation;
+
+ if (operation.Status == EOperationStatus.Succeed)
+ {
+ GameObject prefab = operation.AssetObject as GameObject;
+ Instantiate(prefab);
+ }
+ else
+ {
+ Debug.LogError($"加载失败: {operation.Error}");
+ }
+}
+```
+
+### async/await 方式
+
+```csharp
+async void LoadAssetAsync()
+{
+ var operation = package.LoadAssetAsync("Assets/Prefab.prefab");
+ await operation.Task;
+
+ if (operation.Status == EOperationStatus.Succeed)
+ {
+ GameObject prefab = operation.AssetObject as GameObject;
+ Instantiate(prefab);
+ }
+ else
+ {
+ Debug.LogError($"加载失败: {operation.Error}");
+ }
+}
+```
+
+### 回调方式
+
+```csharp
+void LoadAssetWithCallback()
+{
+ var operation = package.LoadAssetAsync("Assets/Prefab.prefab");
+ operation.Completed += OnLoadCompleted;
+}
+
+void OnLoadCompleted(AsyncOperationBase op)
+{
+ var operation = op as AssetHandle;
+ if (operation.Status == EOperationStatus.Succeed)
+ {
+ GameObject prefab = operation.AssetObject as GameObject;
+ Instantiate(prefab);
+ }
+ else
+ {
+ Debug.LogError($"加载失败: {operation.Error}");
+ }
+}
+```
+
+### 同步等待方式
+
+```csharp
+void LoadAssetSync()
+{
+ var operation = package.LoadAssetAsync("Assets/Prefab.prefab");
+ operation.WaitForAsyncComplete(); // 注意:会阻塞当前线程
+
+ if (operation.Status == EOperationStatus.Succeed)
+ {
+ GameObject prefab = operation.AssetObject as GameObject;
+ Instantiate(prefab);
+ }
+}
+```
+
+### 设置优先级
+
+```csharp
+// 高优先级任务优先执行
+var highPriorityOp = package.LoadAssetAsync("ImportantAsset");
+highPriorityOp.Priority = 100;
+
+var lowPriorityOp = package.LoadAssetAsync("BackgroundAsset");
+lowPriorityOp.Priority = 1;
+```
+
+### 配置时间切片
+
+```csharp
+// 设置每帧最大执行时间为 10 毫秒
+OperationSystem.MaxTimeSlice = 10;
+```
+
+---
+
+## 设计模式
+
+### 模板方法模式
+
+`AsyncOperationBase` 定义操作的骨架流程,子类实现具体步骤:
+
+```
+AsyncOperationBase (抽象类)
+ │
+ ├── StartOperation() ──► InternalStart() [子类实现]
+ ├── UpdateOperation() ──► InternalUpdate() [子类实现]
+ ├── AbortOperation() ──► InternalAbort() [子类可选实现]
+ └── WaitForAsyncComplete() ──► InternalWaitForAsyncComplete() [子类可选实现]
+```
+
+### 状态机模式
+
+操作生命周期通过状态机管理:
+
+```
+┌──────┐ StartOperation() ┌────────────┐
+│ None │ ────────────────────► │ Processing │
+└──────┘ └─────┬──────┘
+ │ UpdateOperation()
+ ┌───────────────┼───────────────┐
+ ▼ ▼ ▼
+ ┌─────────┐ ┌──────────┐ ┌─────────┐
+ │ Succeed │ │ Failed │ │ Aborted │
+ └─────────┘ └──────────┘ └─────────┘
+```
+
+### 组合模式
+
+支持父子任务关系,父任务可以管理多个子任务:
+
+```
+ParentOperation
+ │
+ ├── ChildOperation1
+ ├── ChildOperation2
+ └── ChildOperation3
+```
+
+### 时间切片模式
+
+防止主线程阻塞,每帧限制执行时间:
+
+```
+每帧 Update()
+ │
+ ├── 记录帧开始时间
+ │
+ ├── 遍历调度器(按优先级)
+ │ │
+ │ ├── 遍历操作(按优先级)
+ │ │ │
+ │ │ └── 检查 IsBusy ──► 超时则跳出
+ │ │
+ │ └── 检查 IsBusy ──► 超时则跳出
+ │
+ └── 结束
+```
+
+---
+
+## 类继承关系
+
+```
+AsyncOperationBase (抽象基类)
+ │
+ ├── InitializePackageOperation (包裹初始化)
+ ├── LoadManifestOperation (清单加载)
+ ├── DownloaderOperation (下载器操作)
+ ├── UnloadAllAssetsOperation (卸载所有资源)
+ ├── DestroyPackageOperation (销毁包裹)
+ │
+ ├── ProviderOperation (资源提供者基类)
+ │ ├── AssetProvider (单个资源)
+ │ ├── SubAssetsProvider (子资源)
+ │ ├── AllAssetsProvider (全部资源)
+ │ └── SceneProvider (场景)
+ │
+ └── ... (更多子类)
+
+OperationScheduler
+ └── 管理 List
+
+OperationSystem
+ └── 管理 Dictionary
+```
+
+---
+
+## 调试信息
+
+### 调试属性
+
+在 DEBUG 模式下,`AsyncOperationBase` 提供以下调试信息:
+
+| 属性 | 类型 | 说明 |
+|------|------|------|
+| `StartTime` | `string` | 操作开始时间(格式:HH:MM:SS) |
+| `ElapsedMS` | `long` | 操作耗时(毫秒) |
+
+### DiagnosticOperationInfo
+
+通过 `GetDebugOperationInfo()` 获取操作的诊断信息结构体,包含:
+
+- 操作名称、描述
+- 优先级、进度、状态
+- 开始时间、耗时
+- 子操作列表(递归)
+
+---
+
+## 注意事项
+
+1. **主线程调用**:所有操作的创建和更新应在 Unity 主线程进行
+2. **同步等待风险**:`WaitForAsyncComplete()` 会阻塞当前线程,可能导致死锁,谨慎使用
+3. **优先级变更**:修改 `Priority` 后,下一帧才会重新排序
+4. **时间切片粒度**:时间切片检测在每个操作更新后进行,单个操作内部不会被中断
+5. **子任务中止**:调用 `AbortOperation()` 会递归中止所有子任务
+6. **回调异常隔离**:完成回调中的异常会被捕获并记录,不会影响其他回调
+7. **状态不可逆**:一旦进入终态(Succeed/Failed/Aborted),状态不可更改
+8. **Progress 语义**:`Progress` 在操作完成时自动设置为 1.0f,子类应在处理过程中更新进度
diff --git a/Assets/YooAsset/Runtime/OperationSystem/README.md.meta b/Assets/YooAsset/Runtime/OperationSystem/README.md.meta
new file mode 100644
index 00000000..42a9054c
--- /dev/null
+++ b/Assets/YooAsset/Runtime/OperationSystem/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2afe3d5ffb611b241919112b40d9c7d0
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/Operation/DestroyPackageOperation.cs b/Assets/YooAsset/Runtime/ResourcePackage/Operation/DestroyPackageOperation.cs
index a87a717d..f73f5def 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/Operation/DestroyPackageOperation.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/Operation/DestroyPackageOperation.cs
@@ -1,4 +1,4 @@
-
+
namespace YooAsset
{
public class DestroyPackageOperation : AsyncOperationBase
@@ -39,6 +39,7 @@ namespace YooAsset
{
case EOperationStatus.None:
case EOperationStatus.Failed:
+ case EOperationStatus.Aborted:
_steps = ESteps.DestroyPackage;
break;
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/ResourcePackage.cs b/Assets/YooAsset/Runtime/ResourcePackage/ResourcePackage.cs
index 9b434967..32ed56e8 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/ResourcePackage.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/ResourcePackage.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
@@ -900,6 +900,9 @@ namespace YooAsset
case EOperationStatus.Failed:
string error = _initializeOp == null ? string.Empty : _initializeOp.Error;
throw new YooPackageException(PackageName, $"Resource package initialization failed. Error: {error}");
+
+ case EOperationStatus.Aborted:
+ throw new YooPackageException(PackageName, "Resource package initialization was aborted.");
}
}