From 0d80bcfa5b6b10a9684a64ec9dd9d29aa5991a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Thu, 25 Dec 2025 10:50:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Runtime/Timer/ITimerModule.cs | 2 - Runtime/Timer/TimerModule.cs | 185 ++++++++++++++++++++++++---------- 2 files changed, 134 insertions(+), 53 deletions(-) diff --git a/Runtime/Timer/ITimerModule.cs b/Runtime/Timer/ITimerModule.cs index 2f4ea2d..4d0a683 100644 --- a/Runtime/Timer/ITimerModule.cs +++ b/Runtime/Timer/ITimerModule.cs @@ -9,8 +9,6 @@ namespace AlicizaX { int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args); int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false); - - // 优化1: 使用值类型泛型参数,完全零GC int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false); void Stop(int timerId); void Resume(int timerId); diff --git a/Runtime/Timer/TimerModule.cs b/Runtime/Timer/TimerModule.cs index 5151d3a..f2118aa 100644 --- a/Runtime/Timer/TimerModule.cs +++ b/Runtime/Timer/TimerModule.cs @@ -7,11 +7,8 @@ using UnityEngine; namespace AlicizaX { public delegate void TimerHandler(params object[] args); - public delegate void TimerHandlerNoArgs(); - - // 优化2: 使用union结构存储不同类型的回调(零装箱) [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] [StructLayout(LayoutKind.Sequential, Pack = 4)] @@ -21,20 +18,21 @@ namespace AlicizaX public float TriggerTime; public float Interval; - // 优化3: 使用字段而非object存储,避免装箱 public TimerHandler Handler; public TimerHandlerNoArgs HandlerNoArgs; - public Delegate HandlerGeneric; // 直接存储委托 + public Delegate HandlerGeneric; public bool IsLoop; public bool IsRunning; public bool IsUnscaled; public bool IsActive; - public byte HandlerType; // 0=Handler, 1=NoArgs, 2=Generic, 3=Generic + public byte HandlerType; // 0=Handler, 1=NoArgs, 2=Generic public object[] Args; - public object GenericArg; // 存储泛型参数 + public object GenericArg; + // 优化1: 添加层级信息,用于精确定位 + public int Level; // 当前所在的时间轮层级 public int SlotIndex; public int NextTimerIndex; public int PrevTimerIndex; @@ -48,6 +46,8 @@ namespace AlicizaX Args = null; GenericArg = null; IsActive = false; + Level = -1; + SlotIndex = -1; NextTimerIndex = -1; PrevTimerIndex = -1; } @@ -68,15 +68,9 @@ namespace AlicizaX private int[] _freeIndices; private int _freeCount; - // 优化4: 静态泛型缓存,避免每次调用时的类型检查 - private static class GenericInvoker - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(Delegate callback, object arg) - { - ((Action)callback)((T)arg); - } - } + // 优化2: 延迟删除队列,避免在遍历时修改链表 + private int[] _pendingRemoveTimers; + private int _pendingRemoveCount; private class HierarchicalTimeWheel { @@ -143,6 +137,8 @@ namespace AlicizaX slotIndex = (_levels[2].CurrentSlot + level2Slots) % SLOT_COUNT_LEVEL2; } + // 优化3: 记录层级信息 + timer.Level = level; timer.SlotIndex = slotIndex; timer.NextTimerIndex = _levels[level].SlotHeads[slotIndex]; timer.PrevTimerIndex = -1; @@ -187,21 +183,30 @@ namespace AlicizaX wheelLevel.CurrentSlot = (wheelLevel.CurrentSlot + 1) % wheelLevel.SlotCount; int currentHead = wheelLevel.SlotHeads[wheelLevel.CurrentSlot]; - wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; + wheelLevel.SlotHeads[wheelLevel.CurrentSlot] = -1; // 清空槽 + // 优化4: 遍历时先断开链表,避免在回调中修改导致问题 int currentIndex = currentHead; while (currentIndex != -1) { - int nextIndex = pool[currentIndex].NextTimerIndex; + ref TimerInfo timer = ref pool[currentIndex]; + int nextIndex = timer.NextTimerIndex; - if (pool[currentIndex].IsActive && pool[currentIndex].IsRunning) + // 断开链表连接 + timer.NextTimerIndex = -1; + timer.PrevTimerIndex = -1; + timer.Level = -1; // 标记为不在时间轮中 + + if (timer.IsActive && timer.IsRunning) { if (level == 0) { + // Level 0 直接触发 processTimer(currentIndex); } else { + // 高层级降级到低层级 AddTimer(currentIndex, pool, _currentTime); } } @@ -210,30 +215,53 @@ namespace AlicizaX } } + // 优化5: 安全的移除方法,检查边界 + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveTimer(int poolIndex, TimerInfo[] pool) { + if (poolIndex < 0 || poolIndex >= pool.Length) + return; + ref TimerInfo timer = ref pool[poolIndex]; + // 优化6: 如果已经不在时间轮中,直接返回 + if (timer.Level < 0 || timer.Level >= _levels.Length) + return; + + int level = timer.Level; + int slotIndex = timer.SlotIndex; + + // 边界检查 + if (slotIndex < 0 || slotIndex >= _levels[level].SlotCount) + return; + + // 从链表中移除 if (timer.PrevTimerIndex != -1) { - pool[timer.PrevTimerIndex].NextTimerIndex = timer.NextTimerIndex; + if (timer.PrevTimerIndex < pool.Length) + { + pool[timer.PrevTimerIndex].NextTimerIndex = timer.NextTimerIndex; + } } else { - for (int level = 0; level < _levels.Length; level++) - { - if (_levels[level].SlotHeads[timer.SlotIndex] == poolIndex) - { - _levels[level].SlotHeads[timer.SlotIndex] = timer.NextTimerIndex; - break; - } - } + // 是头节点 + _levels[level].SlotHeads[slotIndex] = timer.NextTimerIndex; } if (timer.NextTimerIndex != -1) { - pool[timer.NextTimerIndex].PrevTimerIndex = timer.PrevTimerIndex; + if (timer.NextTimerIndex < pool.Length) + { + pool[timer.NextTimerIndex].PrevTimerIndex = timer.PrevTimerIndex; + } } + + // 清除链表信息 + timer.Level = -1; + timer.SlotIndex = -1; + timer.NextTimerIndex = -1; + timer.PrevTimerIndex = -1; } } @@ -244,12 +272,16 @@ namespace AlicizaX _timerIdToPoolIndex = new int[1024]; _freeIndices = new int[_timerPoolCapacity]; _freeCount = _timerPoolCapacity; + _pendingRemoveTimers = new int[64]; + _pendingRemoveCount = 0; for (int i = 0; i < _timerPoolCapacity; i++) { _freeIndices[i] = i; _timerPool[i].NextTimerIndex = -1; _timerPool[i].PrevTimerIndex = -1; + _timerPool[i].Level = -1; + _timerPool[i].SlotIndex = -1; } _scaledTimeWheel = new HierarchicalTimeWheel(0.001f); @@ -282,7 +314,6 @@ namespace AlicizaX return timer.TimerId; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public int AddTimer(Action callback, T arg, float time, bool isLoop = false, bool isUnscaled = false) { @@ -292,8 +323,8 @@ namespace AlicizaX timer.TimerId = ++_curTimerId; timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; timer.Interval = isLoop ? time : 0f; - timer.HandlerGeneric = callback; // 直接存储委托,无需包装 - timer.GenericArg = arg; // struct会装箱一次,但只在添加时 + timer.HandlerGeneric = callback; + timer.GenericArg = arg; timer.HandlerType = 2; timer.IsLoop = isLoop; timer.IsRunning = true; @@ -352,6 +383,8 @@ namespace AlicizaX _freeIndices[_freeCount++] = i; _timerPool[i].NextTimerIndex = -1; _timerPool[i].PrevTimerIndex = -1; + _timerPool[i].Level = -1; + _timerPool[i].SlotIndex = -1; } return _freeIndices[--_freeCount]; @@ -371,33 +404,35 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetPoolIndex(int timerId) { - return timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; + return timerId > 0 && timerId < _timerIdToPoolIndex.Length ? _timerIdToPoolIndex[timerId] : -1; } public void Stop(int timerId) { int poolIndex = GetPoolIndex(timerId); - if (poolIndex >= 0 && _timerPool[poolIndex].IsActive) + if (poolIndex >= 0 && poolIndex < _timerPool.Length && _timerPool[poolIndex].IsActive) _timerPool[poolIndex].IsRunning = false; } public void Resume(int timerId) { int poolIndex = GetPoolIndex(timerId); - if (poolIndex >= 0 && _timerPool[poolIndex].IsActive) + if (poolIndex >= 0 && poolIndex < _timerPool.Length && _timerPool[poolIndex].IsActive) _timerPool[poolIndex].IsRunning = true; } public bool IsRunning(int timerId) { int poolIndex = GetPoolIndex(timerId); - return poolIndex >= 0 && _timerPool[poolIndex].IsActive && _timerPool[poolIndex].IsRunning; + return poolIndex >= 0 && poolIndex < _timerPool.Length && + _timerPool[poolIndex].IsActive && _timerPool[poolIndex].IsRunning; } public float GetLeftTime(int timerId) { int poolIndex = GetPoolIndex(timerId); - if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return 0; + if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) + return 0; ref TimerInfo timer = ref _timerPool[poolIndex]; return Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0); @@ -406,25 +441,35 @@ namespace AlicizaX public void Restart(int timerId) { int poolIndex = GetPoolIndex(timerId); - if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return; + if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) + return; ref TimerInfo timer = ref _timerPool[poolIndex]; - timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; + // 先从时间轮中移除 HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.RemoveTimer(poolIndex, _timerPool); + + // 重新计算触发时间并添加 + timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; targetWheel.AddTimer(poolIndex, _timerPool, timer.IsUnscaled ? Time.unscaledTime : Time.time); } public void RemoveTimer(int timerId) { int poolIndex = GetPoolIndex(timerId); - if (poolIndex < 0 || !_timerPool[poolIndex].IsActive) return; + if (poolIndex < 0 || poolIndex >= _timerPool.Length || !_timerPool[poolIndex].IsActive) + return; ref TimerInfo timer = ref _timerPool[poolIndex]; timer.IsActive = false; - HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; - targetWheel.RemoveTimer(poolIndex, _timerPool); + // 优化7: 只有在时间轮中时才移除 + if (timer.Level >= 0) + { + HierarchicalTimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.RemoveTimer(poolIndex, _timerPool); + } ReleaseTimerToPool(poolIndex); } @@ -444,22 +489,37 @@ namespace AlicizaX [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ReleaseTimerToPool(int poolIndex) { - _timerPool[poolIndex].Clear(); - _freeIndices[_freeCount++] = poolIndex; + if (poolIndex >= 0 && poolIndex < _timerPool.Length) + { + _timerPool[poolIndex].Clear(); + + if (_freeCount >= _freeIndices.Length) + { + Array.Resize(ref _freeIndices, _freeIndices.Length * 2); + } + + _freeIndices[_freeCount++] = poolIndex; + } } void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) { _scaledTimeWheel.Advance(Time.time, _timerPool, ProcessTimer); _unscaledTimeWheel.Advance(Time.unscaledTime, _timerPool, ProcessTimer); + + // 优化8: 处理延迟删除队列 + ProcessPendingRemovals(); } - // 优化7: 使用静态泛型分发,避免反射和类型检查 [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessTimer(int poolIndex) { + if (poolIndex < 0 || poolIndex >= _timerPool.Length) + return; + ref TimerInfo timer = ref _timerPool[poolIndex]; - if (!timer.IsActive || !timer.IsRunning) return; + if (!timer.IsActive || !timer.IsRunning) + return; try { @@ -471,18 +531,20 @@ namespace AlicizaX case 1: // NoArgs timer.HandlerNoArgs?.Invoke(); break; - case 2: // Generic - case 3: // Generic - // 直接调用,利用JIT优化 + case 2: // Generic ((dynamic)timer.HandlerGeneric)?.Invoke((dynamic)timer.GenericArg); break; } } catch (Exception e) { - Log.Error($"TimerInfo callback error: {e}"); + Log.Error($"Timer callback error: {e}"); } + // 优化9: 回调后检查是否仍然有效 + if (!timer.IsActive) + return; + if (timer.IsLoop) { timer.TriggerTime += timer.Interval; @@ -491,10 +553,31 @@ namespace AlicizaX } else { - RemoveTimer(timer.TimerId); + // 优化10: 延迟删除,避免在遍历时修改 + AddPendingRemoval(timer.TimerId); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddPendingRemoval(int timerId) + { + if (_pendingRemoveCount >= _pendingRemoveTimers.Length) + { + Array.Resize(ref _pendingRemoveTimers, _pendingRemoveTimers.Length * 2); + } + _pendingRemoveTimers[_pendingRemoveCount++] = timerId; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ProcessPendingRemovals() + { + for (int i = 0; i < _pendingRemoveCount; i++) + { + RemoveTimer(_pendingRemoveTimers[i]); + } + _pendingRemoveCount = 0; + } + void IModule.Dispose() => RemoveAllTimer(); public int Priority => 0;