优化ObjectPool和MemoryPool性能

优化热路径性能 O(1) 避免线性查找
This commit is contained in:
陈思海 2026-04-23 17:50:53 +08:00
parent 46194ddee8
commit d1d86adf09
2 changed files with 265 additions and 80 deletions

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using UnityEngine; using UnityEngine;
@ -14,8 +15,10 @@ namespace AlicizaX.ObjectPool
public T Obj; public T Obj;
public int SpawnCount; public int SpawnCount;
public float LastUseTime; public float LastUseTime;
public int NameHash; public int PrevByName;
public int NextSameName; public int NextByName;
public int PrevAvailableByName;
public int NextAvailableByName;
public int PrevUnused; public int PrevUnused;
public int NextUnused; public int NextUnused;
public byte Flags; public byte Flags;
@ -37,7 +40,9 @@ namespace AlicizaX.ObjectPool
private int m_FreeTop; private int m_FreeTop;
private readonly Dictionary<object, int> m_TargetMap; private readonly Dictionary<object, int> m_TargetMap;
private IntOpenHashMap m_NameMap; private readonly Dictionary<string, int> m_AllNameHeadMap;
private readonly Dictionary<string, int> m_AvailableNameHeadMap;
private readonly Dictionary<string, int> m_AvailableNameTailMap;
private readonly bool m_AllowMultiSpawn; private readonly bool m_AllowMultiSpawn;
private float m_AutoReleaseInterval; private float m_AutoReleaseInterval;
@ -64,7 +69,9 @@ namespace AlicizaX.ObjectPool
m_Slots = new ObjectSlot[initCap]; m_Slots = new ObjectSlot[initCap];
m_FreeStack = new int[initCap]; m_FreeStack = new int[initCap];
m_TargetMap = new Dictionary<object, int>(initCap, AlicizaX.ReferenceComparer<object>.Instance); m_TargetMap = new Dictionary<object, int>(initCap, AlicizaX.ReferenceComparer<object>.Instance);
m_NameMap = new IntOpenHashMap(initCap); m_AllNameHeadMap = new Dictionary<string, int>(initCap, StringComparer.Ordinal);
m_AvailableNameHeadMap = new Dictionary<string, int>(initCap, StringComparer.Ordinal);
m_AvailableNameTailMap = new Dictionary<string, int>(initCap, StringComparer.Ordinal);
m_AllowMultiSpawn = allowMultiSpawn; m_AllowMultiSpawn = allowMultiSpawn;
m_AutoReleaseInterval = autoReleaseInterval; m_AutoReleaseInterval = autoReleaseInterval;
m_Capacity = capacity; m_Capacity = capacity;
@ -139,25 +146,32 @@ namespace AlicizaX.ObjectPool
slot.Obj = obj; slot.Obj = obj;
slot.SpawnCount = spawned ? 1 : 0; slot.SpawnCount = spawned ? 1 : 0;
slot.LastUseTime = Time.realtimeSinceStartup; slot.LastUseTime = Time.realtimeSinceStartup;
slot.NameHash = (obj.Name ?? string.Empty).GetHashCode(); slot.PrevByName = -1;
slot.NextSameName = -1; slot.NextByName = -1;
slot.PrevAvailableByName = -1;
slot.NextAvailableByName = -1;
slot.PrevUnused = -1; slot.PrevUnused = -1;
slot.NextUnused = -1; slot.NextUnused = -1;
slot.SetAlive(true); slot.SetAlive(true);
m_TargetMap[obj.Target] = idx; m_TargetMap[obj.Target] = idx;
if (m_NameMap.TryGetValue(slot.NameHash, out int existingHead)) string objectName = obj.Name ?? string.Empty;
slot.NextSameName = existingHead; if (m_AllNameHeadMap.TryGetValue(objectName, out int existingHead))
m_NameMap.AddOrUpdate(slot.NameHash, idx); {
m_Slots[existingHead].PrevByName = idx;
slot.NextByName = existingHead;
}
m_AllNameHeadMap[objectName] = idx;
obj.LastUseTime = slot.LastUseTime; obj.LastUseTime = slot.LastUseTime;
if (spawned) if (spawned)
obj.OnSpawn(); obj.OnSpawn();
else else
AddToUnusedList(idx); MarkSlotAvailable(idx);
if (Count > m_Capacity) MarkRelease(Count - m_Capacity); if (Count > m_Capacity) MarkRelease(Count - m_Capacity);
ValidateState();
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -166,8 +180,10 @@ namespace AlicizaX.ObjectPool
public T Spawn(string name) public T Spawn(string name)
{ {
if (name == null) throw new GameFrameworkException("Name is invalid."); if (name == null) throw new GameFrameworkException("Name is invalid.");
int nameHash = name.GetHashCode(); if (m_AllowMultiSpawn)
if (!m_NameMap.TryGetValue(nameHash, out int head)) return null; return SpawnAny(name);
if (!m_AvailableNameHeadMap.TryGetValue(name, out int head)) return null;
float now = Time.realtimeSinceStartup; float now = Time.realtimeSinceStartup;
int current = head; int current = head;
@ -175,13 +191,14 @@ namespace AlicizaX.ObjectPool
{ {
ref var slot = ref m_Slots[current]; ref var slot = ref m_Slots[current];
if (slot.IsAlive() && string.Equals(slot.Obj.Name, name) if (slot.IsAlive() && string.Equals(slot.Obj.Name, name)
&& (m_AllowMultiSpawn || slot.SpawnCount == 0)) && slot.SpawnCount == 0)
{ {
SpawnSlot(current, now); SpawnSlot(current, now);
ValidateState();
return slot.Obj; return slot.Obj;
} }
current = slot.NextSameName; current = slot.NextAvailableByName;
} }
return null; return null;
@ -193,17 +210,18 @@ namespace AlicizaX.ObjectPool
public bool CanSpawn(string name) public bool CanSpawn(string name)
{ {
if (name == null) throw new GameFrameworkException("Name is invalid."); if (name == null) throw new GameFrameworkException("Name is invalid.");
int nameHash = name.GetHashCode(); if (m_AllowMultiSpawn)
if (!m_NameMap.TryGetValue(nameHash, out int head)) return false; return m_AllNameHeadMap.ContainsKey(name);
if (!m_AvailableNameHeadMap.TryGetValue(name, out int head)) return false;
int current = head; int current = head;
while (current >= 0) while (current >= 0)
{ {
ref var slot = ref m_Slots[current]; ref var slot = ref m_Slots[current];
if (slot.IsAlive() && string.Equals(slot.Obj.Name, name) if (slot.IsAlive() && slot.SpawnCount == 0 && string.Equals(slot.Obj.Name, name))
&& (m_AllowMultiSpawn || slot.SpawnCount == 0))
return true; return true;
current = slot.NextSameName; current = slot.NextAvailableByName;
} }
return false; return false;
@ -226,6 +244,7 @@ namespace AlicizaX.ObjectPool
} }
UnspawnSlot(idx); UnspawnSlot(idx);
ValidateState();
} }
public override void Release() public override void Release()
@ -258,6 +277,7 @@ namespace AlicizaX.ObjectPool
{ {
m_PendingReleaseCount = Math.Max(0, m_PendingReleaseCount - released); m_PendingReleaseCount = Math.Max(0, m_PendingReleaseCount - released);
ShrinkStorageIfEmpty(); ShrinkStorageIfEmpty();
ValidateState();
} }
} }
@ -301,7 +321,9 @@ namespace AlicizaX.ObjectPool
} }
m_TargetMap.Clear(); m_TargetMap.Clear();
m_NameMap.Clear(); m_AllNameHeadMap.Clear();
m_AvailableNameHeadMap.Clear();
m_AvailableNameTailMap.Clear();
m_SlotCount = 0; m_SlotCount = 0;
m_FreeTop = 0; m_FreeTop = 0;
m_PendingReleaseCount = 0; m_PendingReleaseCount = 0;
@ -309,6 +331,7 @@ namespace AlicizaX.ObjectPool
m_UnusedTail = -1; m_UnusedTail = -1;
m_LastBudgetScanStart = -1; m_LastBudgetScanStart = -1;
m_IsShuttingDown = false; m_IsShuttingDown = false;
ValidateState();
} }
public override ObjectInfo[] GetAllObjectInfos() public override ObjectInfo[] GetAllObjectInfos()
@ -351,7 +374,7 @@ namespace AlicizaX.ObjectPool
{ {
ref var slot = ref m_Slots[idx]; ref var slot = ref m_Slots[idx];
if (slot.SpawnCount == 0) if (slot.SpawnCount == 0)
RemoveFromUnusedList(idx); MarkSlotUnavailable(idx);
slot.SpawnCount++; slot.SpawnCount++;
slot.LastUseTime = now; slot.LastUseTime = now;
@ -371,7 +394,7 @@ namespace AlicizaX.ObjectPool
if (slot.SpawnCount < 0) if (slot.SpawnCount < 0)
throw new GameFrameworkException($"Object '{slot.Obj.Name}' spawn count < 0."); throw new GameFrameworkException($"Object '{slot.Obj.Name}' spawn count < 0.");
if (slot.SpawnCount == 0) if (slot.SpawnCount == 0)
AddToUnusedList(idx); MarkSlotAvailable(idx);
if (Count > m_Capacity && slot.SpawnCount == 0) if (Count > m_Capacity && slot.SpawnCount == 0)
MarkRelease(Count - m_Capacity); MarkRelease(Count - m_Capacity);
} }
@ -409,9 +432,9 @@ namespace AlicizaX.ObjectPool
T obj = slot.Obj; T obj = slot.Obj;
if (slot.SpawnCount == 0) if (slot.SpawnCount == 0)
RemoveFromUnusedList(idx); MarkSlotUnavailable(idx);
RemoveFromNameChain(idx); RemoveFromAllNameChain(idx);
m_TargetMap.Remove(obj.Target); m_TargetMap.Remove(obj.Target);
obj.Release(false); obj.Release(false);
@ -420,8 +443,10 @@ namespace AlicizaX.ObjectPool
slot.Obj = null; slot.Obj = null;
slot.SetAlive(false); slot.SetAlive(false);
slot.SpawnCount = 0; slot.SpawnCount = 0;
slot.NameHash = 0; slot.PrevByName = -1;
slot.NextSameName = -1; slot.NextByName = -1;
slot.PrevAvailableByName = -1;
slot.NextAvailableByName = -1;
slot.PrevUnused = -1; slot.PrevUnused = -1;
slot.NextUnused = -1; slot.NextUnused = -1;
@ -432,34 +457,35 @@ namespace AlicizaX.ObjectPool
ShrinkStorageIfEmpty(); ShrinkStorageIfEmpty();
} }
private void RemoveFromNameChain(int idx) private void RemoveFromAllNameChain(int idx)
{ {
ref var slot = ref m_Slots[idx]; ref var slot = ref m_Slots[idx];
int nameHash = slot.NameHash; string objectName = slot.Obj.Name ?? string.Empty;
if (!m_NameMap.TryGetValue(nameHash, out int head)) return; if (!m_AllNameHeadMap.TryGetValue(objectName, out int head))
return;
if (head == idx) int prev = slot.PrevByName;
int next = slot.NextByName;
if (prev >= 0)
{ {
m_NameMap.Remove(nameHash); m_Slots[prev].NextByName = next;
if (slot.NextSameName >= 0)
m_NameMap.AddOrUpdate(nameHash, slot.NextSameName);
} }
else else
{ {
int current = head; if (head != idx)
while (current >= 0) throw new GameFrameworkException($"Object pool '{FullName}' all-name chain is inconsistent.");
{
ref var chainSlot = ref m_Slots[current]; if (next >= 0)
if (chainSlot.NextSameName == idx) m_AllNameHeadMap[objectName] = next;
{ else
chainSlot.NextSameName = slot.NextSameName; m_AllNameHeadMap.Remove(objectName);
break;
}
current = chainSlot.NextSameName;
}
} }
slot.NextSameName = -1; if (next >= 0)
m_Slots[next].PrevByName = prev;
slot.PrevByName = -1;
slot.NextByName = -1;
} }
private int ReleaseUnused(int maxReleaseCount, bool requireExpired, float expireThreshold) private int ReleaseUnused(int maxReleaseCount, bool requireExpired, float expireThreshold)
@ -530,7 +556,9 @@ namespace AlicizaX.ObjectPool
m_Slots = new ObjectSlot[InitSlotCapacity]; m_Slots = new ObjectSlot[InitSlotCapacity];
m_FreeStack = new int[InitSlotCapacity]; m_FreeStack = new int[InitSlotCapacity];
m_NameMap = new IntOpenHashMap(InitSlotCapacity); m_AllNameHeadMap.Clear();
m_AvailableNameHeadMap.Clear();
m_AvailableNameTailMap.Clear();
m_SlotCount = 0; m_SlotCount = 0;
m_FreeTop = 0; m_FreeTop = 0;
m_UnusedHead = -1; m_UnusedHead = -1;
@ -538,45 +566,112 @@ namespace AlicizaX.ObjectPool
m_LastBudgetScanStart = -1; m_LastBudgetScanStart = -1;
} }
private void AddToUnusedList(int idx) [Conditional("UNITY_EDITOR"), Conditional("DEVELOPMENT_BUILD")]
private void ValidateState()
{
int aliveCount = 0;
int unusedCount = 0;
for (int i = 0; i < m_SlotCount; i++)
{
ref var slot = ref m_Slots[i];
if (!slot.IsAlive())
continue;
aliveCount++;
object target = slot.Obj.Target;
if (!m_TargetMap.TryGetValue(target, out int mappedIdx) || mappedIdx != i)
throw new GameFrameworkException($"Object pool '{FullName}' target index map is inconsistent.");
string objectName = slot.Obj.Name ?? string.Empty;
if (!m_AllNameHeadMap.TryGetValue(objectName, out int head))
throw new GameFrameworkException($"Object pool '{FullName}' all-name head is missing.");
if (slot.PrevByName < 0 && head != i)
throw new GameFrameworkException($"Object pool '{FullName}' all-name chain head is inconsistent.");
if (slot.NextByName >= 0 && m_Slots[slot.NextByName].PrevByName != i)
throw new GameFrameworkException($"Object pool '{FullName}' all-name chain link is inconsistent.");
bool inUnusedList = m_UnusedHead == i || slot.PrevUnused >= 0 || slot.NextUnused >= 0;
bool inAvailableList = false;
if (slot.SpawnCount == 0)
{
unusedCount++;
if (!inUnusedList)
throw new GameFrameworkException($"Object pool '{FullName}' unused list is inconsistent.");
if (!m_AvailableNameHeadMap.TryGetValue(objectName, out int availableHead))
throw new GameFrameworkException($"Object pool '{FullName}' available-name head is missing.");
inAvailableList = availableHead == i || slot.PrevAvailableByName >= 0 || slot.NextAvailableByName >= 0;
if (!inAvailableList)
throw new GameFrameworkException($"Object pool '{FullName}' available-name chain is inconsistent.");
if (slot.NextAvailableByName >= 0 && m_Slots[slot.NextAvailableByName].PrevAvailableByName != i)
throw new GameFrameworkException($"Object pool '{FullName}' available-name link is inconsistent.");
}
else
{
if (inUnusedList)
throw new GameFrameworkException($"Object pool '{FullName}' spawned object exists in unused list.");
if (slot.PrevAvailableByName >= 0 || slot.NextAvailableByName >= 0)
throw new GameFrameworkException($"Object pool '{FullName}' spawned object exists in available chain.");
}
}
if (aliveCount != m_TargetMap.Count)
throw new GameFrameworkException($"Object pool '{FullName}' alive count is inconsistent.");
int walkUnusedCount = 0;
int current = m_UnusedHead;
int prevUnused = -1;
while (current >= 0)
{
ref var slot = ref m_Slots[current];
if (!slot.IsAlive() || slot.SpawnCount != 0)
throw new GameFrameworkException($"Object pool '{FullName}' unused chain contains invalid slot.");
if (slot.PrevUnused != prevUnused)
throw new GameFrameworkException($"Object pool '{FullName}' unused chain linkage is inconsistent.");
walkUnusedCount++;
prevUnused = current;
current = slot.NextUnused;
}
if (walkUnusedCount != unusedCount)
throw new GameFrameworkException($"Object pool '{FullName}' unused chain count is inconsistent.");
}
private void MarkSlotAvailable(int idx)
{
AddToUnusedListTail(idx);
AddToAvailableNameChain(idx);
}
private void MarkSlotUnavailable(int idx)
{
RemoveFromUnusedList(idx);
RemoveFromAvailableNameChain(idx);
}
private void AddToUnusedListTail(int idx)
{ {
ref var slot = ref m_Slots[idx]; ref var slot = ref m_Slots[idx];
if (m_UnusedHead == idx || slot.PrevUnused >= 0 || slot.NextUnused >= 0) if (m_UnusedHead == idx || slot.PrevUnused >= 0 || slot.NextUnused >= 0)
return; return;
if (m_UnusedTail >= 0 && m_Slots[m_UnusedTail].LastUseTime <= slot.LastUseTime)
{
m_Slots[m_UnusedTail].NextUnused = idx;
slot.PrevUnused = m_UnusedTail; slot.PrevUnused = m_UnusedTail;
slot.NextUnused = -1; slot.NextUnused = -1;
m_UnusedTail = idx;
}
else
{
int current = m_UnusedHead;
int prev = -1;
while (current >= 0 && m_Slots[current].LastUseTime <= slot.LastUseTime)
{
prev = current;
current = m_Slots[current].NextUnused;
}
slot.PrevUnused = prev; if (m_UnusedTail >= 0)
slot.NextUnused = current; m_Slots[m_UnusedTail].NextUnused = idx;
if (prev >= 0)
m_Slots[prev].NextUnused = idx;
else else
m_UnusedHead = idx; m_UnusedHead = idx;
if (current >= 0)
m_Slots[current].PrevUnused = idx;
else
m_UnusedTail = idx; m_UnusedTail = idx;
if (m_UnusedTail < 0)
m_UnusedTail = idx;
}
} }
private void RemoveFromUnusedList(int idx) private void RemoveFromUnusedList(int idx)
@ -604,6 +699,75 @@ namespace AlicizaX.ObjectPool
if (m_LastBudgetScanStart == idx) if (m_LastBudgetScanStart == idx)
m_LastBudgetScanStart = next >= 0 ? next : m_UnusedHead; m_LastBudgetScanStart = next >= 0 ? next : m_UnusedHead;
} }
private void AddToAvailableNameChain(int idx)
{
ref var slot = ref m_Slots[idx];
if (slot.PrevAvailableByName >= 0 || slot.NextAvailableByName >= 0)
return;
string objectName = slot.Obj.Name ?? string.Empty;
if (m_AvailableNameTailMap.TryGetValue(objectName, out int tail))
{
m_Slots[tail].NextAvailableByName = idx;
slot.PrevAvailableByName = tail;
slot.NextAvailableByName = -1;
m_AvailableNameTailMap[objectName] = idx;
}
else
{
slot.PrevAvailableByName = -1;
slot.NextAvailableByName = -1;
m_AvailableNameHeadMap[objectName] = idx;
m_AvailableNameTailMap[objectName] = idx;
}
}
private void RemoveFromAvailableNameChain(int idx)
{
ref var slot = ref m_Slots[idx];
string objectName = slot.Obj.Name ?? string.Empty;
if (slot.PrevAvailableByName < 0
&& slot.NextAvailableByName < 0
&& (!m_AvailableNameHeadMap.TryGetValue(objectName, out int head) || head != idx))
{
return;
}
int prev = slot.PrevAvailableByName;
int next = slot.NextAvailableByName;
if (prev >= 0)
m_Slots[prev].NextAvailableByName = next;
else if (next >= 0)
m_AvailableNameHeadMap[objectName] = next;
else
m_AvailableNameHeadMap.Remove(objectName);
if (next >= 0)
m_Slots[next].PrevAvailableByName = prev;
else if (prev >= 0)
m_AvailableNameTailMap[objectName] = prev;
else
m_AvailableNameTailMap.Remove(objectName);
slot.PrevAvailableByName = -1;
slot.NextAvailableByName = -1;
}
private T SpawnAny(string name)
{
if (!m_AllNameHeadMap.TryGetValue(name, out int head))
return null;
float now = Time.realtimeSinceStartup;
ref var slot = ref m_Slots[head];
if (!slot.IsAlive() || !string.Equals(slot.Obj.Name, name, StringComparison.Ordinal))
return null;
SpawnSlot(head, now);
return slot.Obj;
}
} }
} }
} }

View File

@ -8,11 +8,13 @@ namespace AlicizaX.ObjectPool
[UnityEngine.Scripting.Preserve] [UnityEngine.Scripting.Preserve]
internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable internal sealed partial class ObjectPoolService : ServiceBase, IObjectPoolService, IServiceTickable
{ {
private const float DefaultAutoReleaseInterval = float.MaxValue;
private const int DefaultCapacity = int.MaxValue; private const int DefaultCapacity = int.MaxValue;
private const float DefaultExpireTime = float.MaxValue; private const float DefaultExpireTime = float.MaxValue;
private readonly Dictionary<TypeNamePair, ObjectPoolBase> m_ObjectPools; private readonly Dictionary<TypeNamePair, ObjectPoolBase> m_ObjectPools;
private readonly List<ObjectPoolBase> m_ObjectPoolList; private readonly List<ObjectPoolBase> m_ObjectPoolList;
private readonly Dictionary<ObjectPoolBase, int> m_ObjectPoolIndexMap;
private readonly List<ObjectPoolBase> m_CachedAllObjectPools; private readonly List<ObjectPoolBase> m_CachedAllObjectPools;
private readonly Comparison<ObjectPoolBase> m_ObjectPoolComparer; private readonly Comparison<ObjectPoolBase> m_ObjectPoolComparer;
@ -20,6 +22,7 @@ namespace AlicizaX.ObjectPool
{ {
m_ObjectPools = new Dictionary<TypeNamePair, ObjectPoolBase>(); m_ObjectPools = new Dictionary<TypeNamePair, ObjectPoolBase>();
m_ObjectPoolList = new List<ObjectPoolBase>(); m_ObjectPoolList = new List<ObjectPoolBase>();
m_ObjectPoolIndexMap = new Dictionary<ObjectPoolBase, int>(AlicizaX.ReferenceComparer<ObjectPoolBase>.Instance);
m_CachedAllObjectPools = new List<ObjectPoolBase>(); m_CachedAllObjectPools = new List<ObjectPoolBase>();
m_ObjectPoolComparer = ObjectPoolComparer; m_ObjectPoolComparer = ObjectPoolComparer;
} }
@ -41,6 +44,7 @@ namespace AlicizaX.ObjectPool
m_ObjectPoolList[i].Shutdown(); m_ObjectPoolList[i].Shutdown();
m_ObjectPools.Clear(); m_ObjectPools.Clear();
m_ObjectPoolList.Clear(); m_ObjectPoolList.Clear();
m_ObjectPoolIndexMap.Clear();
m_CachedAllObjectPools.Clear(); m_CachedAllObjectPools.Clear();
} }
@ -117,12 +121,13 @@ namespace AlicizaX.ObjectPool
var pool = new ObjectPool<T>( var pool = new ObjectPool<T>(
options.Name ?? string.Empty, options.Name ?? string.Empty,
options.AllowMultiSpawn, options.AllowMultiSpawn,
options.AutoReleaseInterval ?? DefaultExpireTime, options.AutoReleaseInterval ?? DefaultAutoReleaseInterval,
options.Capacity ?? DefaultCapacity, options.Capacity ?? DefaultCapacity,
options.ExpireTime ?? DefaultExpireTime, options.ExpireTime ?? DefaultExpireTime,
options.Priority); options.Priority);
m_ObjectPools.Add(key, pool); m_ObjectPools.Add(key, pool);
m_ObjectPoolIndexMap.Add(pool, m_ObjectPoolList.Count);
m_ObjectPoolList.Add(pool); m_ObjectPoolList.Add(pool);
return pool; return pool;
} }
@ -138,12 +143,13 @@ namespace AlicizaX.ObjectPool
var pool = (ObjectPoolBase)Activator.CreateInstance(poolType, var pool = (ObjectPoolBase)Activator.CreateInstance(poolType,
options.Name ?? string.Empty, options.Name ?? string.Empty,
options.AllowMultiSpawn, options.AllowMultiSpawn,
options.AutoReleaseInterval ?? DefaultExpireTime, options.AutoReleaseInterval ?? DefaultAutoReleaseInterval,
options.Capacity ?? DefaultCapacity, options.Capacity ?? DefaultCapacity,
options.ExpireTime ?? DefaultExpireTime, options.ExpireTime ?? DefaultExpireTime,
options.Priority); options.Priority);
m_ObjectPools.Add(key, pool); m_ObjectPools.Add(key, pool);
m_ObjectPoolIndexMap.Add(pool, m_ObjectPoolList.Count);
m_ObjectPoolList.Add(pool); m_ObjectPoolList.Add(pool);
return pool; return pool;
} }
@ -217,12 +223,27 @@ namespace AlicizaX.ObjectPool
if (m_ObjectPools.TryGetValue(key, out var pool)) if (m_ObjectPools.TryGetValue(key, out var pool))
{ {
pool.Shutdown(); pool.Shutdown();
m_ObjectPoolList.Remove(pool); RemovePoolFromList(pool);
m_ObjectPoolIndexMap.Remove(pool);
return m_ObjectPools.Remove(key); return m_ObjectPools.Remove(key);
} }
return false; return false;
} }
private void RemovePoolFromList(ObjectPoolBase pool)
{
if (!m_ObjectPoolIndexMap.TryGetValue(pool, out int index))
return;
int lastIndex = m_ObjectPoolList.Count - 1;
ObjectPoolBase lastPool = m_ObjectPoolList[lastIndex];
m_ObjectPoolList[index] = lastPool;
m_ObjectPoolList.RemoveAt(lastIndex);
if (!ReferenceEquals(lastPool, pool))
m_ObjectPoolIndexMap[lastPool] = index;
}
private static void ValidateObjectType(Type objectType) private static void ValidateObjectType(Type objectType)
{ {
if (objectType == null) if (objectType == null)