本来想在重构 Timer 时候的使用,但是为了保留链式编程风格,就还是没能把这个对象池用上。但写了半天,扔掉可惜了,写在这里呢,希望能对读者有啥启发最好了。
核心代码
using System;
using System.Collections.Concurrent;
public interface IPoolable
{
/// <summary>
/// 对象池 用于标记层别是否可被回收
/// </summary>
AllocateState AllocateState { get; set; }
}
public enum AllocateState
{
InUse,//使用中
Recycled //已回收
}
public sealed class ObjectPool<T> where T : IPoolable
{
internal ConcurrentStack<T> items; //线程安全 栈
private Func<T> factory; //埋入的实例化 规则
public int Count //池子有多少货
{
get
{
return items.Count;
}
}
public int Capacity { get; set; } //池子有多大
internal ObjectPool(Func<T> factory,int capacity = 100)
{
this.factory = factory;
this.Capacity = capacity;
items = new ConcurrentStack<T>();
}
public void Clear()
{
items.Clear();
}
public T Allocate() //分配
{
T item = default(T);
if (items.IsEmpty || !items.TryPop(out item))
{
item =factory.Invoke();
}
item.AllocateState = AllocateState.InUse; //标记为使用中
return item;
}
public void Release(T target) //释放
{
//池爆炸了再多都不再要了,当然,如果不是 InUse 的就更别想挤进来~
if (target.AllocateState.Equals(AllocateState.InUse) && items.Count < Capacity)
{
items.Push(target);
}
}
}
Tips:
- 使用了泛型 T 实现了类型安全;
- 使用了 ConcurrentStack 线程安全栈,微软底层帮处理了可能存在的并发问题 ;
- 控制反转 : Func<T> factory ,可以预先埋入实例化逻辑,
- 使用 IPoolable 接口标记可回收的对象
使用场景(伪代码)
public class A : IPoolable //这个是可回收对象
{
public AllocateState AllocateState{get;set;}
public string feild;
public void RestData() // 重置数据
{
feild = string.Empty;
}
}
public class B //这个类 演示了怎么使用这个 对象池
{
public void Test()
{
ObjectPool<A> pool = new ObjectPool<A>(InstantiateRule); //造个池子,埋入实例化规则
A a = pool.Allocate(); //分配对象
a.feild = "假装在使用这个对象";
a.RestData(); //重置数据准备回收啦
pool.Release(a); //回收到对象池
}
private A InstantiateRule()//实例化规则
{
return new A(); //当然可以写更多定制化的东西在里面
}
}
扩展阅读:
- GitHub/UnityCsReference/ObjectPool.cs (Unity官方公开的源码)
- System.Reflection.Internal.ObjectPool<T>
- C# 队列和栈 线程安全 - 天才卧龙 - 博客园
- Unity 游戏框架搭建 (二十) 更安全的对象池 - 凉鞋的笔记 - 博客园
网友评论