1. 程式人生 > >.NET Core 3.0之深入原始碼理解ObjectPool(二)

.NET Core 3.0之深入原始碼理解ObjectPool(二)

寫在前面

前文主要介紹了ObjectPool的一些理論基礎,本文主要從原始碼角度理解Microsoft.Extensions.ObjectPool是如何實現的。下圖為其三大核心元件圖:

核心元件

ObjectPool

ObjectPool是一個泛型抽象類,裡面只有兩個抽象方法,Get和Return。它從底層定義了最一般的介面。

  • Get方法用於從物件池獲取到可用物件,如果物件不可用則建立物件並返回出來
  • Return方法使用者將物件返回到物件池

原始碼如下:

   1:  public abstract class ObjectPool<T> where T : class
   2:  {
   3:      /// <summary>
   4:      /// Gets an object from the pool if one is available, otherwise creates one.
   5:      /// </summary>
   6:      /// <returns>A <typeparamref name="T"/>.</returns>
   7:      public abstract T Get();
   8:   
   9:      /// <summary>
  10:      /// Return an object to the pool.
  11:      /// </summary>
  12:      /// <param name="obj">The object to add to the pool.</param>
  13:      public abstract void Return(T obj);
  14:  }

ObjectPoolProvider

ObjectPoolProvider也是抽象類,其內部內建了一個已經實現的Create泛型方法以及一個抽象Create方法,這代表兩種ObjectPool的建立方式,一個是基於預設策略的,一個是基於使用者自定義策略的。

   1:  public abstract class ObjectPoolProvider
   2:  {
   3:      /// <summary>
   4:      /// Creates an <see cref="ObjectPool"/>.
   5:      /// </summary>
   6:      /// <typeparam name="T">The type to create a pool for.</typeparam>
   7:      public ObjectPool<T> Create<T>() where T : class, new()
   8:      {
   9:          return Create<T>(new DefaultPooledObjectPolicy<T>());
  10:      }
  11:   
  12:      /// <summary>
  13:      /// Creates an <see cref="ObjectPool"/> with the given <see cref="IPooledObjectPolicy{T}"/>.
  14:      /// </summary>
  15:      /// <typeparam name="T">The type to create a pool for.</typeparam>
  16:      public abstract ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T : class;
  17:  }

IPooledObjectPolicy

這個介面是一個泛型介面,用於提供一種策略來管理物件池中的物件,同樣也有兩個方法,Create和Return。

  • Create方法用於建立相關型別例項
  • Return方法用於將已經使用好的物件放回到物件池的時候進行邏輯處理,包括物件的狀態重置以及是否能夠放回到物件池
   1:  public interface IPooledObjectPolicy<T>
   2:  {
   3:      /// <summary>
   4:      /// Create a <typeparamref name="T"/>.
   5:      /// </summary>
   6:      /// <returns>The <typeparamref name="T"/> which was created.</returns>
   7:      T Create();
   8:   
   9:      /// <summary>
  10:      /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool.
  11:      /// </summary>
  12:      /// <param name="obj">The object to return to the pool.</param>
  13:      /// <returns><code>true</code> if the object should be returned to the pool. <code>false</code> if it's not possible/desirable for the pool to keep the object.</returns>
  14:      bool Return(T obj);
  15:  }

該介面有一個實現PooledObjectPolicy,這是一個抽象類,內部有兩個抽象方法:

   1:  public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T>
   2:  {
   3:      public abstract T Create();
   4:   
   5:      public abstract bool Return(T obj);
   6:  }

實現機制

其內部實現邏輯較為簡單,充分考慮到了一般實現、物件追蹤、物件釋放等場景的使用方式。

以下為其邏輯圖:

DefaultObjectPool

DefaultObjectPool實現了ObjectPool,其內部維護了一個結構體型別的私有陣列,用於儲存相關物件。該陣列的大小在建構函式中定義,其實際大小為輸入值減去1(預設情況下,其值為邏輯處理器數量的兩倍)主要是因為DefaultObjectPool單獨將首項定義了出來。

以下為DefaultObjectPool中Get和Return的實現:

   1:  public override T Get()
   2:  {
   3:      var item = _firstItem;
   4:      if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)
   5:      {
   6:          var items = _items;
   7:          for (var i = 0; i < items.Length; i++)
   8:          {
   9:              item = items[i].Element;
  10:              if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)
  11:              {
  12:                  return item;
  13:              }
  14:          }
  15:   
  16:          item = Create();
  17:      }
  18:   
  19:      return item;
  20:  }
  21:   
  22:  public override void Return(T obj)
  23:  {
  24:      if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))
  25:      {
  26:          if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)
  27:          {
  28:              var items = _items;
  29:              for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)
  30:              {
  31:              }
  32:          }
  33:      }
  34:  }

通過原始碼可以知道這兩個方法大量使用了Interlocked.CompareExchange:

   1:  public static int CompareExchange(
   2:      ref int location1,
   3:      int value,
   4:      int comparand
   5:  )

比較location1與comparand,如果不相等,什麼都不做;如果location1與comparand相等,則用value替換location1的值。無論比較結果相等與否,返回值都是location1中原有的值。

Interlocked.CompareExchange的使用確保了執行緒安全性。

DefaultObjectPoolProvider

DefaultObjectPoolProvider實現了ObjectPoolProvider,該類重寫了Create方法並返回ObjectPool物件。該類還定義了MaximumRetained屬性,預設情況下,其值為邏輯處理器數量的兩倍。

其原始碼如下,比較簡單:

   1:  public class DefaultObjectPoolProvider : ObjectPoolProvider
   2:  {
   3:      /// <summary>
   4:      /// The maximum number of objects to retain in the pool.
   5:      /// </summary>
   6:      public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2;
   7:   
   8:      /// <inheritdoc/>
   9:      public override ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy)
  10:      {
  11:          if (policy == null)
  12:          {
  13:              throw new ArgumentNullException(nameof(policy));
  14:          }
  15:   
  16:          if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
  17:          {
  18:              return new DisposableObjectPool<T>(policy, MaximumRetained);
  19:          }
  20:   
  21:          return new DefaultObjectPool<T>(policy, MaximumRetained);
  22:      }
  23:  }

其中DisposableObjectPool是DefaultObjectPool類的派生類,這個類也實現了IDisposable,用於建立可手動釋放的ObjectPool物件。

其相關程式碼如下:

   1:  public void Dispose()
   2:  {
   3:      _isDisposed = true;
   4:   
   5:      DisposeItem(_firstItem);
   6:      _firstItem = null;
   7:   
   8:      ObjectWrapper[] items = _items;
   9:      for (var i = 0; i < items.Length; i++)
  10:      {
  11:          DisposeItem(items[i].Element);
  12:          items[i].Element = null;
  13:      }
  14:  }
  15:   
  16:  private void DisposeItem(T item)
  17:  {
  18:      if (item is IDisposable disposable)
  19:      {
  20:          disposable.Dispose();
  21:      }
  22:  }

DefaultPooledObjectPolicy

該類繼承了PooledObjectPolicy,實現也非常簡單。

不過值得注意的是,PooledObjectPolicy還有一個實現StringBuilderPooledObjectPolicy,這個類從命名上看就知道是基於StringBuilder的。其內部預設定義了StringBuilder的大小以及初始化容量。並確定了超出容量後,將不允許歸還物件。

在我們自定義PooledObjectPolicy的時候,可以參考這段實現去擴充套件新的PooledObjectPolicy物件。

我們看一下原始碼:

   1:  public class StringBuilderPooledObjectPolicy : PooledObjectPolicy<StringBuilder>
   2:  {
   3:      public int InitialCapacity { get; set; } = 100;
   4:   
   5:      public int MaximumRetainedCapacity { get; set; } = 4 * 1024;
   6:   
   7:      public override StringBuilder Create()
   8:      {
   9:          return new StringBuilder(InitialCapacity);
  10:      }
  11:   
  12:      public override bool Return(StringBuilder obj)
  13:      {
  14:          if (obj.Capacity > MaximumRetainedCapacity)
  15:          {
  16:              // Too big. Discard this one.
  17:              return false;
  18:          }
  19:   
  20:          obj.Clear();
  21:          return true;
  22:      }
  23:  }

物件追蹤

該庫內部定義了LeakTrackingObjectPool和LeakTrackingObjectPoolProvider用於追蹤物件狀態。

  • LeakTrackingObjectPoolProvider會根據建構函式傳入的ObjectPoolProvider型別物件,建立LeakTrackingObjectPool例項。
  • LeakTrackingObjectPool內部定義了ConditionalWeakTable<T, Tracker>型別的陣列,MSDN的解釋是使編譯器可以將物件欄位動態附加到託管物件,這個物件會自動維護內部的鍵值對,而不會一直使其停留在記憶體中。

Tracker是LeakTrackingObjectPool的內部類,其目的是為了方便我們對物件本身進行維護跟蹤,其定義如下:

   1:  private class Tracker : IDisposable
   2:  {
   3:      private readonly string _stack;
   4:      private bool _disposed;
   5:   
   6:      public Tracker()
   7:      {
   8:          _stack = Environment.StackTrace;
   9:      }
  10:   
  11:      public void Dispose()
  12:      {
  13:          _disposed = true;
  14:          GC.SuppressFinalize(this);
  15:      }
  16:   
  17:      ~Tracker()
  18:      {
  19:          if (!_disposed && !Environment.HasShutdownStarted)
  20:          {
  21:              Debug.Fail($"{typeof(T).Name} was leaked. Created at: {Environment.NewLine}{_stack}");
  22:          }
  23:      }
  24:  }