.NET Core 3.0之深入源碼理解ObjectPool(一)


寫在前面

對象池是一種比較常用的提高系統性能的軟件設計模式,它維護了一系列相關對象列表的容器對象,這些對象可以隨時重復使用,對象池節省了頻繁創建對象的開銷。

它使用取用/歸還-重復取用的操作模式,如下圖所示:

objectpoolsc

本文將主要介紹對象池的基本概念、對象池的優勢及其工作機制,下一篇文檔將從源碼角度介紹.NET Core 3.0是如何實現對象池的。

對象池基礎

對象池的基本概念

對象池的核心概念是容器,其表示形式可以認為是列表。每當有新的對象創建請求進入時,都會通過從池中分配一個對象來滿足該請求。當我們需要獲取某個對象時,可以從池中獲取。既然有了對象池,那么也就很方便我們就很容易建立起對象的管理與追蹤了了。

objectpoolsc2

對象池的優勢

我們知道一旦應用程序啟動並運行,內存使用就會受到系統所需對象的數量和大小的影響。

我們知道創建一個對象的實例,是需要消耗一定的系統資源,尤其是該對象的構造十分復雜的時候,再加上需要頻繁創建的時候,其實例化所消耗的資源更加昂貴。如果我們能有一種辦法減少這種昂貴的系統開銷,這對系統性能的提升是十分有幫助的。

對象池理念的出現,有助於我們解決復雜對象的重復創建所引發的資源開銷問題。對象存儲在某種類型的列表或者說數組中,我們可以和獲取數組中的子項一樣獲取已經存在在對象池中的對象。

對象池的最大優點是,它可以自主管理內部已經創建的對象,包括回收和重復使用對象。程序在使用完某個對象后,會將其發還至對象池,而不是在內存中銷毀他們。

對象池通過資源的分配,因而也就減少了應用程序所需的垃圾回收數量。這對於需要頻繁創建同一對象的功能來說,對象池最大程度地減少了系統資源的消耗。

簡單來說,對象池的設計目標就是要使對象可以得到重復使用,而不是被垃圾回收器回收。

對象池的工作機制

當客戶端程序需要某個對象時,對象池首先嘗試提供一個已經創建的對象。如果沒有可用的對象,則會創建一個新對象。這類似於一個GetOrAdd的操作​。同時對象池中對象的數量就會減少,直到該對象已經使用完,那么它就會被放回到對象池池中以等待使用。這就是為什么對象池有助於重用性、並減少了在獲取對象時創建對象所涉及的開銷的原因。

另外,需要注意的是,只要池中至少有一個對象,該池就會一直保留在內存中。只要對象池還在,里面的對象也會一直存在。

當對象池用於並發操作時,需要確保對象池是線程安全的,而且其本身還要有很高的性能。

ConcurrentBag對象池解決方案

這個解決方案來自於MSDN,ConcurrentBag <T>用於存儲對象,因為它支持快速插入和刪除,尤其是在同一線程同時添加和刪除項目時。該示例可以進一步擴展為圍繞IProducerConsumerCollection <T>構建,該數據由bag數據結構實現,ConcurrentQueue <T>ConcurrentStack <T>也是如此

   1:  using System;
   2:  using System.Collections.Concurrent;
   3:  using System.Threading;
   4:  using System.Threading.Tasks;
   5:   
   6:   
   7:  namespace ObjectPoolExample
   8:  {
   9:      public class ObjectPool<T>
  10:      {
  11:          private ConcurrentBag<T> _objects;
  12:          private Func<T> _objectGenerator;
  13:   
  14:          public ObjectPool(Func<T> objectGenerator)
  15:          {
  16:              if (objectGenerator == null) throw new ArgumentNullException("objectGenerator");
  17:              _objects = new ConcurrentBag<T>();
  18:              _objectGenerator = objectGenerator;
  19:          }
  20:   
  21:          public T GetObject()
  22:          {
  23:              T item;
  24:              if (_objects.TryTake(out item)) return item;
  25:              return _objectGenerator();
  26:          }
  27:   
  28:          public void PutObject(T item)
  29:          {
  30:              _objects.Add(item);
  31:          }
  32:      }
  33:   
  34:      class Program
  35:      {
  36:         static void Main(string[] args)
  37:          {
  38:              CancellationTokenSource cts = new CancellationTokenSource();
  39:   
  40:              // Create an opportunity for the user to cancel.
  41:              Task.Run(() =>
  42:                  {
  43:                      if (Console.ReadKey().KeyChar == 'c' || Console.ReadKey().KeyChar == 'C')
  44:                          cts.Cancel();
  45:                  });
  46:   
  47:              ObjectPool<MyClass> pool = new ObjectPool<MyClass> (() => new MyClass());            
  48:   
  49:              // Create a high demand for MyClass objects.
  50:              Parallel.For(0, 1000000, (i, loopState) =>
  51:                  {
  52:                      MyClass mc = pool.GetObject();
  53:                      Console.CursorLeft = 0;
  54:                      // This is the bottleneck in our application. All threads in this loop
  55:                      // must serialize their access to the static Console class.
  56:                      Console.WriteLine("{0:####.####}", mc.GetValue(i));                 
  57:                      
  58:                      pool.PutObject(mc);
  59:                      if (cts.Token.IsCancellationRequested)
  60:                          loopState.Stop();                 
  61:   
  62:                  });
  63:              Console.WriteLine("Press the Enter key to exit.");
  64:              Console.ReadLine();
  65:              cts.Dispose();
  66:          }
  67:   
  68:      }
  69:   
  70:      // A toy class that requires some resources to create.
  71:      // You can experiment here to measure the performance of the
  72:      // object pool vs. ordinary instantiation.
  73:      class MyClass
  74:      {
  75:          public int[] Nums {get; set;}
  76:          public double GetValue(long i)
  77:          {
  78:              return Math.Sqrt(Nums[i]);
  79:          }
  80:          public MyClass()
  81:          {
  82:              Nums = new int[1000000];
  83:              Random rand = new Random();
  84:              for (int i = 0; i < Nums.Length; i++)
  85:                  Nums[i] = rand.Next();
  86:          }
  87:      }   
  88:  }

參考鏈接:https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool


免責聲明!

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



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