當我們頻繁創建刪除大量對象的時候,對象的創建刪除所造成的開銷就不容小覷了。為了提高性能,我們往往需要實現一個對象池作為Cache:使用對象時,它從池中提取。用完對象時,它放回池中。從而減少創建對象的開銷。
由於.net BCL庫中並沒有對象池的標准實現,因此需要我們自己去實現。好在實現功能簡單的對象池並不麻煩,一般幾十行代碼就能實現。需要注意的一點是,對象池大多是需要支持多線程訪問的,因此需要考慮線程安全問題。
在.Net 4.0后,BCL在System.Collections.Concurrent名字空間下引入了一系列線程安全的對象,微軟甚至在MSDN上給了一個通過實現對象池的簡單示例:How to: Create an Object Pool by Using a ConcurrentBag.
這個例子本身沒有什么問題,但如果放在實際生產中就覺得有點簡單過頭了,一般還需要加上容量限制和重用時進行reset操作。這里我就稍微將其改了下:

1 public class ObjectPool<T> 2 { 3 ConcurrentBag<T> buffer; 4 Func<T> createFunc; 5 Action<T> resetFunc; 6 7 public ObjectPool(Func<T> createFunc, Action<T> resetFunc, int capacity) 8 { 9 Contract.Assume(createFunc != null); 10 Contract.Assume(capacity > 0); 11 12 this.buffer = new ConcurrentBag<T>(); 13 this.createFunc = createFunc; 14 this.resetFunc = resetFunc; 15 16 this.Capacity = capacity; 17 } 18 19 public int Capacity { get; private set; } 20 public int Count { get { return buffer.Count; } } 21 22 /// <summary> 23 /// 申請對象 24 /// </summary> 25 public T GetObject() 26 { 27 var obj = default(T); 28 29 if (!buffer.TryTake(out obj)) 30 return createFunc(); 31 else 32 return obj; 33 } 34 35 /// <summary> 36 /// 釋放對象 37 /// </summary> 38 public void PutObject(T obj) 39 { 40 Contract.Assume(obj != null); 41 42 if (Count >= Capacity) //超過容量了,不再需要 43 return; 44 45 if (resetFunc != null) 46 resetFunc(obj); 47 48 buffer.Add(obj); 49 } 50 }
需要注意的是,我這里的實現並沒有完全確保Capacity的絕對性:當兩個線程同時往一個即將到達上限的對象池中放置對象時,可能都會成功。因為我覺得這個是沒有太大必要的,感興趣的朋友可以把它改下。