設計模式之美:Object Pool(對象池)


索引

意圖

運用對象池化技術可以顯著地提升性能,尤其是當對象的初始化過程代價較大或者頻率較高時。

Object pooling can offer a significant performance boost; it is most effective in situations where the cost of initializing a class instance is high, the rate of instantiation of a class is high.

結構

參與者

Reusable

  • 類的實例與其他對象進行有限時間的交互。

ReusablePool

  • 管理類的實例。

Client

  • 使用類的實例。

適用性

當以下情況成立時可以使用 Object Pool 模式:

  • 類的實例可重用於交互。
  • 類的實例化過程開銷較大。
  • 類的實例化的頻率較高。
  • 類參與交互的時間周期有限。

效果

  • 節省了創建類的實例的開銷。
  • 節省了創建類的實例的時間。
  • 存儲空間隨着對象的增多而增大。

相關模式

  • 通常,可以使用 Singleton 模式實現 ReusablePool 類。
  • Factory Method 模式封裝了對象的創建的過程,但其不負責管理對象。Object Pool 負責管理對象。

實現

實現方式(一):實現 DatabaseConnectionPool 類。

如果 Client 調用 ObjectPool 的 AcquireReusable() 方法來獲取 Reusable 對象,當在 ObjectPool 中存在可用的 Reusable 對象時,其將一個 Reusable 從池中移除,然后返回該對象。如果池為空,則 ObjectPool 會創建一個新的 Reusable 對象。

  1 namespace ObjectPoolPattern.Implementation1
  2 {
  3   public abstract class ObjectPool<T>
  4   {
  5     private TimeSpan _expirationTime;
  6     private Dictionary<T, DateTime> _unlocked;
  7     private Dictionary<T, DateTime> _locked;
  8     private readonly object _sync = new object();
  9 
 10     public ObjectPool()
 11     {
 12       _expirationTime = TimeSpan.FromSeconds(30);
 13       _locked = new Dictionary<T, DateTime>();
 14       _unlocked = new Dictionary<T, DateTime>();
 15     }
 16 
 17     public ObjectPool(TimeSpan expirationTime)
 18       : this()
 19     {
 20       _expirationTime = expirationTime;
 21     }
 22 
 23     protected abstract T Create();
 24 
 25     public abstract bool Validate(T reusable);
 26 
 27     public abstract void Expire(T reusable);
 28 
 29     public T CheckOut()
 30     {
 31       lock (_sync)
 32       {
 33         T reusable = default(T);
 34 
 35         if (_unlocked.Count > 0)
 36         {
 37           foreach (var item in _unlocked)
 38           {
 39             if ((DateTime.UtcNow - item.Value) > _expirationTime)
 40             {
 41               // object has expired
 42               _unlocked.Remove(item.Key);
 43               Expire(item.Key);
 44             }
 45             else
 46             {
 47               if (Validate(item.Key))
 48               {
 49                 // find a reusable object
 50                 _unlocked.Remove(item.Key);
 51                 _locked.Add(item.Key, DateTime.UtcNow);
 52                 reusable = item.Key;
 53                 break;
 54               }
 55               else
 56               {
 57                 // object failed validation
 58                 _unlocked.Remove(item.Key);
 59                 Expire(item.Key);
 60               }
 61             }
 62           }
 63         }
 64 
 65         // no object available, create a new one
 66         if (reusable == null)
 67         {
 68           reusable = Create();
 69           _locked.Add(reusable, DateTime.UtcNow);
 70         }
 71 
 72         return reusable;
 73       }
 74     }
 75 
 76     public void CheckIn(T reusable)
 77     {
 78       lock (_sync)
 79       {
 80         _locked.Remove(reusable);
 81         _unlocked.Add(reusable, DateTime.UtcNow);
 82       }
 83     }
 84   }
 85 
 86   public class DatabaseConnection : IDisposable
 87   {
 88     // do some heavy works
 89     public DatabaseConnection(string connectionString)
 90     {
 91     }
 92 
 93     public bool IsOpen { get; set; }
 94 
 95     // release something
 96     public void Dispose()
 97     {
 98     }
 99   }
100 
101   public class DatabaseConnectionPool : ObjectPool<DatabaseConnection>
102   {
103     private string _connectionString;
104 
105     public DatabaseConnectionPool(string connectionString)
106       : base(TimeSpan.FromMinutes(1))
107     {
108       this._connectionString = connectionString;
109     }
110 
111     protected override DatabaseConnection Create()
112     {
113       return new DatabaseConnection(_connectionString);
114     }
115 
116     public override void Expire(DatabaseConnection connection)
117     {
118       connection.Dispose();
119     }
120 
121     public override bool Validate(DatabaseConnection connection)
122     {
123       return connection.IsOpen;
124     }
125   }
126 
127   public class Client
128   {
129     public static void TestCase1()
130     {
131       // Create the ConnectionPool:
132       DatabaseConnectionPool pool = new DatabaseConnectionPool(
133         "Data Source=DENNIS;Initial Catalog=TESTDB;Integrated Security=True;");
134 
135       // Get a connection:
136       DatabaseConnection connection = pool.CheckOut();
137 
138       // Use the connection
139 
140       // Return the connection:
141       pool.CheckIn(connection);
142     }
143   }
144 }

實現方式(二):使用對象構造方法和預分配方式實現 ObjectPool 類。

 1 namespace ObjectPoolPattern.Implementation2
 2 {
 3   /// <summary>
 4   /// 對象池
 5   /// </summary>
 6   /// <typeparam name="T">對象類型</typeparam>
 7   public class ObjectPool<T> where T : class
 8   {
 9     private readonly Func<T> _objectFactory;
10     private readonly ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
11 
12     /// <summary>
13     /// 對象池
14     /// </summary>
15     /// <param name="objectFactory">構造緩存對象的函數</param>
16     public ObjectPool(Func<T> objectFactory)
17     {
18       _objectFactory = objectFactory;
19     }
20 
21     /// <summary>
22     /// 構造指定數量的對象
23     /// </summary>
24     /// <param name="count">數量</param>
25     public void Allocate(int count)
26     {
27       for (int i = 0; i < count; i++)
28         _queue.Enqueue(_objectFactory());
29     }
30 
31     /// <summary>
32     /// 緩存一個對象
33     /// </summary>
34     /// <param name="obj">對象</param>
35     public void Enqueue(T obj)
36     {
37       _queue.Enqueue(obj);
38     }
39 
40     /// <summary>
41     /// 獲取一個對象
42     /// </summary>
43     /// <returns>對象</returns>
44     public T Dequeue()
45     {
46       T obj;
47       return !_queue.TryDequeue(out obj) ? _objectFactory() : obj;
48     }
49   }
50 
51   class Program
52   {
53     static void Main(string[] args)
54     {
55       var pool = new ObjectPool<byte[]>(() => new byte[65535]);
56       pool.Allocate(1000);
57 
58       var buffer = pool.Dequeue();
59 
60       // .. do something here ..
61 
62       pool.Enqueue(buffer);
63     }
64   }
65 }

設計模式之美》為 Dennis Gao 發布於博客園的系列文章,任何未經作者本人同意的人為或爬蟲轉載均為耍流氓。


免責聲明!

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



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