對象分配也有樂觀和悲觀

作者: 四有青年  來源: 博客園  發布時間: 2010-12-06 10:04  閱讀: 696 次  推薦: 0   原文鏈接   [收藏]  

   最近做公司的網游項目,使用JAVA開發,網絡通訊這塊使用的則是mina框架。說實話,到現在也只是知道怎么用,內部實現還沒仔細研究過。跟成熟的框架比起來,感覺自己之前在.NET上寫的Socket在結構化程度上稍差了點,作為一名C# er,便有了模仿其API完善.NET類庫的想法。這其中涉及很多技術細節,如線程同步、異步通訊、TCP協議解析、對象池使用及管理等。今天要講的主題便是這個對象池了。
   之所以要用到對象池,是因為在一個典型的TCP/IP應用中,在服務器運行期間,會有無數次收到消息,格式化成指定對象(這里暫時叫Packet吧),并且服務器處理后組織特定的Packet返回給客戶端。這樣如果每次都用new操作來創建Packet對象的話,加上對象的GC處理回收時間,會對性能產生一定影響。如果服務器壓力不大的話,確實可以不用對象池,這樣可以保持代碼的簡潔。不過我的看法是,即使暫時你的服務器壓力瓶頸不在這,我們也可以先在底層提供這樣的功能,作為技術積累,搞程序還是有點適當的前瞻意識比較好。
   對象池,最簡單的功能當然就是實現對象復用,避免反復創建及銷毀對象。查閱了一些文章,個人總結主要的區別集中在管理策略及管理途徑上。策略這東西,可以很簡單,可以很復雜,因情況而異。比如.NET中的線程池中關于線程的管理策略,可細分為繁忙時的創建策略及空閑時的銷毀。我們的目標是實現一個盡量通用的對象池,因此,用到的管理策略應該是盡量的簡單。
   再說說管理途徑,一般有兩種方式:
     1、通過封裝對象并附加一些池管理需要參數,如是否空閑,最后一次使用時間等,可以稱之為ObjectWrapper;
     2、通過讓對象實現特定接口來實現復用,如實現IDisposable接口或自己另外定義接口。
 以上兩種方法各有利弊,第一種可以額外附加不同種類的參數,達到輔助管理的目的,第二種可能功能稍微單一,但是貴在簡潔,這里我選的是后者,并讓池化對象實現一個IPoolable接口,同時定義對象池對象接口IObjectPool,代碼如下

    public interface IPoolable
    {
        
/// <summary>
        /// 將對象釋放回對象池,務必確保事件等關聯資源先清除
        
/// </summary>
        void Free();

        
/// <summary>
        /// 管理自身的對象池的引用
        
/// </summary>
        IObjectPool<IPoolable> Pool { getset; }
    }

    
public interface IObjectPool<T> where T : IPoolable
    {
        
/// <summary>
        /// 獲取空閑對象
        
/// </summary>
        T GetFreeObject();

        
/// <summary>
        /// 釋放對象回對象池
        
/// </summary>
        void ReleaseObject(T item);
    }

  Free方法要求池化對象的實現者將自身返回給對象池的空閑列表,這樣做的好處在于對象的使用者可以不用關注對象池的細節。

   對象的管理這一問題,確實傷了不少腦筋。使用動態管理,我認為太過復雜,如果這么做,至少要做到可銷毀對象的定義,怎么樣才算可銷毀,是對象空閑了還是一定時間沒用了,還是當前數量超出了某個值等。上層應用使用不同對象的情況各有差異,如果簡單的用一個標準定死,很難通用。
   另一種比較常見的方式,我稱之為“樂觀獲取”,這種做法在無可用對象,并且對象總數達到數量上限時,會假定所有對象的使用者會及時的返回對象,此時請求者選擇Sleep或者WaitOne等方式等待。我認為這是種雪上加霜的解決辦法,當所有可用對象已經被使用,并且達到了數量上限,這時候認為系達到對象使用的賣方市場,即供不應求。即使有限的對象被返回,也不能滿足更多的需求,最壞的情況是一些請求者一直等待。
   基于此,我選擇了“悲觀獲取”的方式,這種做法在請求對象時,保證返回一個正確對象,雖然這有可能造成系統中對象總數暫時超出了數量限制,至少可以保證不會因為沒可用對象而使上層邏輯受阻。而當系統壓力減輕時,可通過控制返回對象的邏輯來銷毀多余的對象。

對象池代碼如下:

 
代碼
    /// <summary>
    /// 無需等待的對象池
    
/// </summary>
    class UnWaitObjectPool<T> : IObjectPool<T> where T : IPoolable, new()
    {
        
private int _minObjCount;
        
private int _maxObjCount;
        
private object _mutex;
        
private Queue<T> _freeQueue;//空閑隊列

        private UnWaitObjectPool(int min, int max)
        {
            
if (min > max || min < 0 || max < 0)
            {
                
throw new ArgumentException("illegal args.");
            }

            
this._minObjCount = min;
            
this._maxObjCount = max;
            
this._mutex = new object();
            
this._freeQueue = new Queue<T>();

            
for (int i = 0; i < min; i++)
            {
                
this._freeQueue.Enqueue(new T());
            }
        }

        
/// <summary>
        /// 有空閑對象則直接取出,如果沒有,直接創建,這里無視數量上限
        
/// </summary>
        /// <returns></returns>
        public T GetFreeObject()
        {
            
if (this._freeQueue.Count > 0)
            {
                
lock (this._mutex)
                {
                    
if (this._freeQueue.Count > 0)
                    {
                        
return this._freeQueue.Dequeue();
                    }
                }
            }

            
return new T();
        }

        
/// <summary>
        /// 返回對象,每次調用后如果空閑對象超出限制,則銷毀
        
/// </summary>
        /// <param name="item"></param>
        public void ReleaseObject(T item)
        {
            
lock (this._mutex)
            {
                
this._freeQueue.Enqueue(item);
            }

            
this.TryTrimToMax();
        }

        
private void TryTrimToMax()
        {
            
while (this._freeQueue.Count > this._maxObjCount)
            {
                
this._freeQueue.Dequeue();
            }
        }
    }

  是不是簡單很多?這里少了不必要的WaitOne等線程同步操作,以及復雜的管理策略。通俗的說,這里也許跟傳統概念的對象池不太一樣,因為沒有嚴格控制對象的創建,因此算是一種折中的方案,當對象需求緊張時,對象池與普通new方法一起發揮作用,兩種機制同時服務于上層應用。而當系統壓力一般時,對象池獨自承擔對象的維護。
   這里只給出部分代碼,忽略了單例的部分,對象池對象本身是單例的!但是我們的目的是作為簡單或者默認的對象池,前文提到的Packet只是一種應用場景,既然設計成泛型,肯定是希望盡量的通用,因此如果有遺漏的地方沒想到,歡迎大家批評及拍磚。

 參考文章:
 Java對象池技術的原理及其實現http://www.cnblogs.com/aurawing/articles/1887029.html
 使用.net技術創建一個小型對象池http://www.cnblogs.com/chegan/archive/2004/03/04/2129.html
 SocketAsyncEventArgs對象池示例http://msdn.microsoft.com/en-us/library/bb551675.aspx
 數組緩存池示例http://msdn.microsoft.com/en-us/library/bb517542.aspx

0
0
 
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()