Lock Free ObjectPool<T>的C#实现 (对象池)

最近实现了一个LockFree的对象池

主要的想法是:

  1.复用构造出来的对象
  2.避免重复创建和销毁对象对GC造成的压力

  3.避免重复创建对象造成的资源消耗

最适合的场景是:

  1.构造对象很慢,并且需要构造很多个对象的情况

主要技术特征:

  1. C#4.0的实现(可以降级到2.0)

  2. 内部没有使用普通的lock,而是使用Lock Free的实现方式

  3. 将常用的取回对象的方式,换成委托运行, 这样的话ObjectPool可以帮你自动将对象压回队列

  4. ObjectPool支持对象池上下限的设置(如果移除该功能性能还会提高很多!)

以下是实现代码:

    /// <summary>
/// 对象池 (有上下限的版本..如果不要控制上限的话性还能好很多)
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class ObjectPool<T>
{
#region private Fields 为了调试方便 吧一些变量弄成了Public, 实际使用中请关掉
private int isTaked = 0;
private Queue<T> queue = new Queue<T>();
private Func<T> func = null;
private int currentResource = 0;
public int tryNewObject = 0;
private int minSize = 0;
private int maxSize = 0;
#endregion

#region private methods
private void Enter()
{
while (Interlocked.Exchange(ref isTaked, 1) != 0)
{
}
}
private void Leave()
{
Thread.VolatileWrite(ref isTaked, 0);
}
#endregion

/// <summary>
/// 构造一个对象池
/// </summary>
/// <param name="func">用来初始化对象的函数</param>
/// <param name="minSize">对象池下限</param>
/// <param name="maxSize">对象池上限</param>
public ObjectPool(Func<T> func, int minSize = 1, int maxSize = 5)
{
if (func == null)
{
throw new ArgumentNullException("func");
}
if (minSize < 0)
{
throw new ArgumentOutOfRangeException("minSize");
}
if (maxSize < 0)
{
throw new ArgumentOutOfRangeException("maxSize");
}
if (maxSize < minSize)
{
throw new ArgumentException("maxSize can not be less than minSize");
}

this.minSize = minSize;
this.maxSize = maxSize;
for (int i = 0; i < minSize; i++)
{
this.queue.Enqueue(func());
}

this.currentResource = minSize;
this.tryNewObject = minSize;
this.func = func;
}

/// <summary>
/// 从对象池中取一个对象出来,并执行, 执行完成以后会自动将对象放回池中
/// </summary>
/// <param name="action">一个可用的对象</param>
public void Run(Action<T> action)
{
if (action == null)
{
throw new ArgumentNullException("func");
}
T t = default(T);
try
{
Start:
if (Interlocked.Decrement(ref this.currentResource) < 0)
{
if (this.tryNewObject < this.maxSize)
{
Interlocked.Increment(ref this.tryNewObject);
t = func();
Interlocked.Increment(ref this.currentResource);
}
else
{
Interlocked.Increment(ref this.currentResource);
goto Start;
}
}
else
{
this.Enter();
t = this.queue.Dequeue();
this.Leave();
}

action(t);
}
finally
{
this.Enter();
this.queue.Enqueue(t);
this.Leave();
Interlocked.Increment(ref currentResource);
}
}

/// <summary>
/// 看看现在的Queue中有多少个资源,线程不安全...
/// </summary>
[Obsolete]
public int ResourceCountInQueue { get { return queue.Count(); } }
}



测试代码如下:(注意测试代码是4.0的)

       static void Main(string[] args)
{
//测试代码
int length = 1 * 1000 * 1000;
Stopwatch sw = Stopwatch.StartNew();
ObjectPool<MD5> pool = new ObjectPool<MD5>(() =>
{
Thread.Sleep(1000);//模拟缓慢的构造情况
return new MD5CryptoServiceProvider();
});

Parallel.For(0, length, p =>
{
pool.Run(md5 =>
{
md5.ComputeHash(Guid.NewGuid().ToByteArray());//模拟一个运算场景
});
});
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
Console.WriteLine(length);
Console.WriteLine(pool.ResourceCountInQueue);
}

 

顺便测试一下机器性能...这个代码在我机器上大约要跑10秒钟......- -#

 

欢迎大家指正~

posted on 2012-03-16 22:22  听说读写  阅读(3709)  评论(5编辑  收藏  举报

导航