Lock Free ObjectPool 的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秒鍾......- -#

 

歡迎大家指正~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM