refactor : 重构异步操作模块

This commit is contained in:
何冠峰
2026-01-08 11:21:26 +08:00
parent f0563cce0b
commit 3dd3d4ef76
6 changed files with 347 additions and 109 deletions

View File

@@ -9,7 +9,6 @@ namespace YooAsset
public abstract class AsyncOperationBase : IEnumerator, IComparable<AsyncOperationBase> public abstract class AsyncOperationBase : IEnumerator, IComparable<AsyncOperationBase>
{ {
private Action<AsyncOperationBase> _callback; private Action<AsyncOperationBase> _callback;
private string _packageName = null;
private int _whileFrame = 1000; private int _whileFrame = 1000;
/// <summary> /// <summary>
@@ -47,17 +46,6 @@ namespace YooAsset
/// </summary> /// </summary>
public float Progress { get; protected set; } public float Progress { get; protected set; }
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName
{
get
{
return _packageName;
}
}
/// <summary> /// <summary>
/// 是否已经完成 /// 是否已经完成
/// </summary> /// </summary>
@@ -76,11 +64,25 @@ namespace YooAsset
{ {
add add
{ {
if (value == null)
return;
if (IsDone) if (IsDone)
{
try
{
value.Invoke(this); value.Invoke(this);
}
catch (Exception ex)
{
YooLogger.Error($"Exception in completion callback: {ex}");
}
}
else else
{
_callback += value; _callback += value;
} }
}
remove remove
{ {
_callback -= value; _callback -= value;
@@ -118,14 +120,6 @@ namespace YooAsset
return string.Empty; return string.Empty;
} }
/// <summary>
/// 设置包裹名称
/// </summary>
internal void SetPackageName(string packageName)
{
_packageName = packageName;
}
/// <summary> /// <summary>
/// 添加子任务 /// 添加子任务
/// </summary> /// </summary>
@@ -194,8 +188,6 @@ namespace YooAsset
if (IsDone && IsFinish == false) if (IsDone && IsFinish == false)
{ {
IsFinish = true; IsFinish = true;
// 进度百分百完成
Progress = 1f; Progress = 1f;
// 结束记录 // 结束记录
@@ -229,10 +221,28 @@ namespace YooAsset
if (IsDone == false) if (IsDone == false)
{ {
InternalAbort();
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = "user abort"; Error = "user abort";
YooLogger.Warning($"Async operaiton {this.GetType().Name} has been abort !"); YooLogger.Warning($"Async operaiton {this.GetType().Name} has been abort !");
InternalAbort(); }
}
/// <summary>
/// 结束异步任务
/// </summary>
internal void FinishOperation()
{
if (IsFinish == false)
{
IsFinish = true;
Progress = 1f;
// 结束记录
DebugEndRecording();
if (_taskCompletionSource != null)
_taskCompletionSource.TrySetResult(null);
} }
} }

View File

@@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// 异步操作调度器
/// </summary>
internal class OperationScheduler : IComparable<OperationScheduler>
{
private readonly List<AsyncOperationBase> _operations = new List<AsyncOperationBase>(100);
private readonly List<AsyncOperationBase> _newList = new List<AsyncOperationBase>(100);
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 调度器优先级(值越大越优先)
/// </summary>
public int Priority { private set; get; }
/// <summary>
/// 创建顺序(用于同优先级稳定排序)
/// </summary>
public int CreateIndex { private set; get; }
public OperationScheduler(string packageName, int priority, int createIndex)
{
PackageName = packageName;
Priority = priority;
CreateIndex = createIndex;
}
/// <summary>
/// 开始处理异步操作
/// </summary>
public void StartOperation(AsyncOperationBase operation)
{
_newList.Add(operation);
operation.StartOperation();
// 通知开始回调
OperationSystem.InvokeStartCallback(PackageName, operation);
}
/// <summary>
/// 更新调度器
/// </summary>
public void Update()
{
// 移除已经完成的异步操作
for (int i = _operations.Count - 1; i >= 0; i--)
{
var operation = _operations[i];
if (operation.IsFinish)
{
_operations.RemoveAt(i);
// 通知完成回调
OperationSystem.InvokeFinishCallback(PackageName, operation);
}
}
// 添加新增的异步操作
if (_newList.Count > 0)
{
bool sorting = false;
foreach (var operation in _newList)
{
if (operation.Priority > 0)
{
sorting = true;
break;
}
}
_operations.AddRange(_newList);
_newList.Clear();
// 重新排序优先级
if (sorting)
_operations.Sort();
}
// 更新进行中的异步操作
for (int i = 0; i < _operations.Count; i++)
{
// 检查全局时间切片预算
if (OperationSystem.IsBusy)
break;
var operation = _operations[i];
if (operation.IsFinish)
continue;
operation.UpdateOperation();
}
}
/// <summary>
/// 清空并中止所有任务
/// </summary>
public void ClearAll()
{
// 终止临时队列里的任务
foreach (var operation in _newList)
{
operation.AbortOperation();
}
_newList.Clear();
// 终止正在进行的任务
foreach (var operation in _operations)
{
operation.AbortOperation();
}
_operations.Clear();
}
/// <summary>
/// 获取调试信息
/// </summary>
public List<DebugOperationInfo> GetDebugOperationInfos()
{
int totalCount = _operations.Count + _newList.Count;
List<DebugOperationInfo> result = new List<DebugOperationInfo>(totalCount);
// 包含正在执行的任务
foreach (var operation in _operations)
{
var operationInfo = OperationSystem.GetDebugOperationInfo(operation);
result.Add(operationInfo);
}
// 包含待处理的新任务
foreach (var operation in _newList)
{
var operationInfo = OperationSystem.GetDebugOperationInfo(operation);
result.Add(operationInfo);
}
return result;
}
#region
public int CompareTo(OperationScheduler other)
{
// 优先级高的排前面
int result = other.Priority.CompareTo(this.Priority);
if (result == 0)
{
// 优先级相同,按创建顺序
result = this.CreateIndex.CompareTo(other.CreateIndex);
}
return result;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 919380cb845bb8146a03ed154644b89f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -15,8 +15,14 @@ namespace YooAsset
} }
#endif #endif
private static readonly List<AsyncOperationBase> _operations = new List<AsyncOperationBase>(1000); // 全局调度器名称
private static readonly List<AsyncOperationBase> _newList = new List<AsyncOperationBase>(1000); public const string GLOBAL_SCHEDULER_NAME = "";
private static readonly Dictionary<string, OperationScheduler> _schedulerDic = new Dictionary<string, OperationScheduler>(100);
private static readonly List<OperationScheduler> _schedulerList = new List<OperationScheduler>(100);
private static bool _schedulerListDirty = false;
private static int _createIndex = 0;
private static Action<string, AsyncOperationBase> _startCallback = null; private static Action<string, AsyncOperationBase> _startCallback = null;
private static Action<string, AsyncOperationBase> _finishCallback = null; private static Action<string, AsyncOperationBase> _finishCallback = null;
@@ -25,12 +31,12 @@ namespace YooAsset
private static long _frameTime; private static long _frameTime;
/// <summary> /// <summary>
/// 异步操作的最小时间片段 /// 异步操作系统的每帧最大执行预算(毫秒)
/// </summary> /// </summary>
public static long MaxTimeSlice { set; get; } = long.MaxValue; public static long MaxTimeSlice { set; get; } = long.MaxValue;
/// <summary> /// <summary>
/// 处理器是否繁忙 /// 异步操作系统是否繁忙
/// </summary> /// </summary>
public static bool IsBusy public static bool IsBusy
{ {
@@ -39,7 +45,10 @@ namespace YooAsset
if (_watch == null) if (_watch == null)
return false; return false;
// NOTE : 单次调用开销约1微秒 if (MaxTimeSlice == long.MaxValue)
return false;
// 注意 : 单次调用开销约1微秒
return _watch.ElapsedMilliseconds - _frameTime >= MaxTimeSlice; return _watch.ElapsedMilliseconds - _frameTime >= MaxTimeSlice;
} }
} }
@@ -51,6 +60,9 @@ namespace YooAsset
public static void Initialize() public static void Initialize()
{ {
_watch = Stopwatch.StartNew(); _watch = Stopwatch.StartNew();
// 创建全局调度器
CreatePackageScheduler(GLOBAL_SCHEDULER_NAME, 0);
} }
/// <summary> /// <summary>
@@ -58,54 +70,23 @@ namespace YooAsset
/// </summary> /// </summary>
public static void Update() public static void Update()
{ {
// 移除已经完成的异步操作 // 重新排序调度器
// 注意:移除上一帧完成的异步操作,方便调试器接收到完整的信息! if (_schedulerListDirty)
for (int i = _operations.Count - 1; i >= 0; i--)
{ {
var operation = _operations[i]; _schedulerListDirty = false;
if (operation.IsFinish) _schedulerList.Sort();
{
_operations.RemoveAt(i);
if (_finishCallback != null)
_finishCallback.Invoke(operation.PackageName, operation);
}
} }
// 添加新增的异步操作 // 更新帧时间
if (_newList.Count > 0)
{
bool sorting = false;
foreach (var operation in _newList)
{
if (operation.Priority > 0)
{
sorting = true;
break;
}
}
_operations.AddRange(_newList);
_newList.Clear();
// 重新排序优先级
if (sorting)
_operations.Sort();
}
// 更新进行中的异步操作
bool checkBusy = MaxTimeSlice < long.MaxValue;
_frameTime = _watch.ElapsedMilliseconds; _frameTime = _watch.ElapsedMilliseconds;
for (int i = 0; i < _operations.Count; i++)
// 更新调度器
for (int i = 0; i < _schedulerList.Count; i++)
{ {
if (checkBusy && IsBusy) if (IsBusy)
break; break;
var operation = _operations[i]; _schedulerList[i].Update();
if (operation.IsFinish)
continue;
operation.UpdateOperation();
} }
} }
@@ -114,8 +95,16 @@ namespace YooAsset
/// </summary> /// </summary>
public static void DestroyAll() public static void DestroyAll()
{ {
_operations.Clear(); // 清空所有调度器
_newList.Clear(); foreach (var scheduler in _schedulerList)
{
scheduler.ClearAll();
}
_schedulerDic.Clear();
_schedulerList.Clear();
_schedulerListDirty = false;
_createIndex = 0;
_startCallback = null; _startCallback = null;
_finishCallback = null; _finishCallback = null;
_watch = null; _watch = null;
@@ -123,28 +112,48 @@ namespace YooAsset
MaxTimeSlice = long.MaxValue; MaxTimeSlice = long.MaxValue;
} }
/// <summary>
/// 创建包裹调度器
/// </summary>
internal static void CreatePackageScheduler(string packageName, int priority)
{
if (_schedulerDic.ContainsKey(packageName))
{
throw new YooInternalException($"Package scheduler already exists: {packageName}");
}
var scheduler = new OperationScheduler(packageName, priority, _createIndex++);
_schedulerDic.Add(packageName, scheduler);
_schedulerList.Add(scheduler);
_schedulerListDirty = true;
}
/// <summary>
/// 销毁包裹调度器
/// </summary>
internal static void DestroyPackageScheduler(string packageName)
{
// 不允许销毁默认调度器
if (packageName == GLOBAL_SCHEDULER_NAME)
{
throw new YooInternalException("Cannot destroy the global package scheduler!");
}
if (_schedulerDic.TryGetValue(packageName, out var scheduler))
{
scheduler.ClearAll();
_schedulerDic.Remove(packageName);
_schedulerList.Remove(scheduler);
}
}
/// <summary> /// <summary>
/// 销毁包裹的所有任务 /// 销毁包裹的所有任务
/// </summary> /// </summary>
public static void ClearPackageOperation(string packageName) public static void ClearPackageOperation(string packageName)
{ {
// 终止临时队列里的任务 var scheduler = GetScheduler(packageName);
foreach (var operation in _newList) scheduler.ClearAll();
{
if (operation.PackageName == packageName)
{
operation.AbortOperation();
}
}
// 终止正在进行的任务
foreach (var operation in _operations)
{
if (operation.PackageName == packageName)
{
operation.AbortOperation();
}
}
} }
/// <summary> /// <summary>
@@ -152,12 +161,8 @@ namespace YooAsset
/// </summary> /// </summary>
public static void StartOperation(string packageName, AsyncOperationBase operation) public static void StartOperation(string packageName, AsyncOperationBase operation)
{ {
_newList.Add(operation); var scheduler = GetScheduler(packageName);
operation.SetPackageName(packageName); scheduler.StartOperation(operation);
operation.StartOperation();
if (_startCallback != null)
_startCallback.Invoke(packageName, operation);
} }
/// <summary> /// <summary>
@@ -176,20 +181,55 @@ namespace YooAsset
_finishCallback = callback; _finishCallback = callback;
} }
/// <summary>
/// 触发任务开始回调
/// </summary>
internal static void InvokeStartCallback(string packageName, AsyncOperationBase operation)
{
_startCallback?.Invoke(packageName, operation);
}
/// <summary>
/// 触发任务完成回调
/// </summary>
internal static void InvokeFinishCallback(string packageName, AsyncOperationBase operation)
{
_finishCallback?.Invoke(packageName, operation);
}
/// <summary>
/// 获取调度器(严格模式)
/// </summary>
private static OperationScheduler GetScheduler(string packageName)
{
// 空包名路由到默认调度器
if (string.IsNullOrEmpty(packageName))
packageName = GLOBAL_SCHEDULER_NAME;
if (_schedulerDic.TryGetValue(packageName, out var scheduler))
{
return scheduler;
}
// 严格模式:非默认包裹必须先创建调度器
throw new YooInternalException($"Package scheduler not found: {packageName}. Please call YooAssets.CreatePackage() first!");
}
#region #region
internal static List<DebugOperationInfo> GetDebugOperationInfos(string packageName) internal static List<DebugOperationInfo> GetDebugOperationInfos(string packageName)
{ {
List<DebugOperationInfo> result = new List<DebugOperationInfo>(_operations.Count); // 空包名路由到默认调度器
foreach (var operation in _operations) if (string.IsNullOrEmpty(packageName))
packageName = GLOBAL_SCHEDULER_NAME;
if (_schedulerDic.TryGetValue(packageName, out var scheduler))
{ {
if (operation.PackageName == packageName) return scheduler.GetDebugOperationInfos();
{
var operationInfo = GetDebugOperationInfo(operation);
result.Add(operationInfo);
} }
return new List<DebugOperationInfo>();
} }
return result;
}
internal static DebugOperationInfo GetDebugOperationInfo(AsyncOperationBase operation) internal static DebugOperationInfo GetDebugOperationInfo(AsyncOperationBase operation)
{ {
var operationInfo = new DebugOperationInfo(); var operationInfo = new DebugOperationInfo();

View File

@@ -108,9 +108,6 @@ namespace YooAsset
_downloadingMaxNumber = UnityEngine.Mathf.Clamp(downloadingMaxNumber, 1, MAX_LOADER_COUNT); ; _downloadingMaxNumber = UnityEngine.Mathf.Clamp(downloadingMaxNumber, 1, MAX_LOADER_COUNT); ;
_failedTryAgain = failedTryAgain; _failedTryAgain = failedTryAgain;
// 设置包裹名称 (fix #210)
SetPackageName(packageName);
// 统计下载信息 // 统计下载信息
CalculatDownloaderInfo(); CalculatDownloaderInfo();
} }

View File

@@ -114,6 +114,16 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="packageName">包裹名称</param> /// <param name="packageName">包裹名称</param>
public static ResourcePackage CreatePackage(string packageName) public static ResourcePackage CreatePackage(string packageName)
{
return CreatePackage(packageName, 0);
}
/// <summary>
/// 创建资源包裹
/// </summary>
/// <param name="packageName">包裹名称</param>
/// <param name="packagePriority">包裹优先级(值越大越优先更新)</param>
public static ResourcePackage CreatePackage(string packageName, int packagePriority)
{ {
CheckException(packageName); CheckException(packageName);
if (ContainsPackage(packageName)) if (ContainsPackage(packageName))
@@ -122,6 +132,10 @@ namespace YooAsset
YooLogger.Log($"Create resource package : {packageName}"); YooLogger.Log($"Create resource package : {packageName}");
ResourcePackage package = new ResourcePackage(packageName); ResourcePackage package = new ResourcePackage(packageName);
_packages.Add(package); _packages.Add(package);
// 注册包裹调度器
OperationSystem.CreatePackageScheduler(packageName, packagePriority);
return package; return package;
} }
@@ -185,6 +199,10 @@ namespace YooAsset
} }
YooLogger.Log($"Remove resource package : {packageName}"); YooLogger.Log($"Remove resource package : {packageName}");
// 先销毁调度器,再移除包裹
OperationSystem.DestroyPackageScheduler(packageName);
_packages.Remove(package); _packages.Remove(package);
return true; return true;
} }