GVKun编程网logo

ASP.NET Core 中的 ObjectPool 对象重用(二)(asp.net core 重定向)

21

这篇文章主要围绕ASP.NETCore中的ObjectPool对象重用和二展开,旨在为您提供一份详细的参考资料。我们将全面介绍ASP.NETCore中的ObjectPool对象重用的优缺点,解答二的相

这篇文章主要围绕ASP.NET Core 中的 ObjectPool 对象重用展开,旨在为您提供一份详细的参考资料。我们将全面介绍ASP.NET Core 中的 ObjectPool 对象重用的优缺点,解答的相关问题,同时也会为您带来.NET Core 3.0之深入源码理解ObjectPool(一)、.NET Core 3.0之深入源码理解ObjectPool(二)、.NET Core 中对象池 Object Pool的使用、.NET Core中Object Pool的多种用法详解的实用方法。

本文目录一览:

ASP.NET Core 中的 ObjectPool 对象重用(二)(asp.net core 重定向)

ASP.NET Core 中的 ObjectPool 对象重用(二)(asp.net core 重定向)

前言

上一篇文章主要介绍了 ObjectPool 的理论知识,再来介绍一下 Microsoft.Extensions.ObjectPool 是如何实现的.

image

核心组件

ObjectPool

ObjectPool 是一个泛型抽象接口,他抽象了两个方法 Get 和 Return

  • Get 方法用于从对象池获取到可用对象,如果对象不可用则创建对象并返回出来
  • Return 方法用户将对象返回到对象池

    /// <summary>
    /// A pool of objects.
    /// </summary>
    /// <typeparam name="T">The type of objects to pool.</typeparam>
    public abstract class ObjectPool<T> where T : class
    {
        /// <summary>
        /// Gets an object from the pool if one is available, otherwise creates one.
        /// </summary>
        /// <returns>A <typeparamref name="T"/>.</returns>
        public abstract T Get();

        /// <summary>
        /// Return an object to the pool.
        /// </summary>
        /// <param name="obj">The object to add to the pool.</param>
        public abstract void Return(T obj);
    }


ObjectPoolProvider

ObjectPoolProvider 是一个抽象接口他内置了 Create 的泛型方法和 Create 的泛型抽象方法,他是一个基于默认策略的。


    /// <summary>
    /// A provider of <see cref="ObjectPool{T}"/> instances.
    /// </summary>
    public abstract class ObjectPoolProvider
    {
        /// <summary>
        /// Creates an <see cref="ObjectPool"/>.
        /// </summary>
        /// <typeparam name="T">The type to create a pool for.</typeparam>
        public ObjectPool<T> Create<T>() where T : class, new()
        {
            return Create<T>(new DefaultPooledObjectPolicy<T>());
        }

        /// <summary>
        /// Creates an <see cref="ObjectPool"/> with the given <see cref="IPooledObjectPolicy{T}"/>.
        /// </summary>
        /// <typeparam name="T">The type to create a pool for.</typeparam>
        public abstract ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T : class;
    }
    

IPooledObjectPolicy

IPooledObjectPolicy 是一个泛型接口,提供策略管理对象池,该类也定义了两个方法 CreateReturn 以提供策略实现

  • Create 用于创建相关的类实例
  • Return 用于将已经使用完的对象放回到池中,包括重置对象状态以及是否能够放回到池中

    /// <summary>
    /// Represents a policy for managing pooled objects.
    /// </summary>
    /// <typeparam name="T">The type of object which is being pooled.</typeparam>
    public interface IPooledObjectPolicy<T>
    {
        /// <summary>
        /// Create a <typeparamref name="T"/>.
        /// </summary>
        /// <returns>The <typeparamref name="T"/> which was created.</returns>
        T Create();

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="obj">The object to return to the pool.</param>
        /// <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>
        bool Return(T obj);
    }

PooledObjectPolicy 是一个泛型抽象类,并且实现了 IPooledObjectPolicy,对外提供了两个抽象方法


    public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T>
    {
        public abstract T Create();

        public abstract bool Return(T obj);
    }

实现机制

DefaultObjectPool

DefaultObjectPool 实现了 ObjectPool,Interlocked.CompareExchange (ref _firstItem, null, item) 将_firstItem 的值和 item 的值比较,相等则用 null 替换_firstItem,否则不操作,不管替换还是不替换返回的都是原来保存在_firstItem 的值。

Interlocked 可以为多个线程共享的变量提供原子操作。

  • Interlocked.Increment:以原子操作的形式递增指定变量的值并存储结果。
  • Interlocked.Decrement 以原子操作的形式递减指定变量的值并存储结果。
  • Interlocked.Add 以原子操作的形式,添加两个整数并用两者的和替换第一个整数
        public override T Get()
        {
            var item = _firstItem;
            if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)
            {
                var items = _items;
                for (var i = 0; i < items.Length; i++)
                {
                    item = items[i].Element;
                    if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)
                    {
                        return item;
                    }
                }

                item = Create();
            }

            return item;
        }

        public override void Return(T obj)
        {
            if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))
            {
                if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)
                {
                    var items = _items;
                    for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)
                    {
                    }
                }
            }
        }

DefaultObjectPoolProvider

DefaultObjectPoolProvider 重写了 ObjectPoolProvider 中 Crearte 方法, 设置了默认的对象最大数量只能用的是默认的 Environment.ProcessorCount * 2 (CPU 处理器的两倍)


    /// <summary>
    /// The default <see cref="ObjectPoolProvider"/>.
    /// </summary>
    public class DefaultObjectPoolProvider : ObjectPoolProvider
    {
        /// <summary>
        /// The maximum number of objects to retain in the pool.
        /// </summary>
        public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2;

        /// <inheritdoc/>
        public override ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy)
        {
            if (policy == null)
            {
                throw new ArgumentNullException(nameof(policy));
            }

            if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
            {
                return new DisposableObjectPool<T>(policy, MaximumRetained);
            }

            return new DefaultObjectPool<T>(policy, MaximumRetained);
        }
    }


DisposableObjectPool

DisposableObjectPool 继承了 DefaultObjectPool 以及实现了 IDisposable 用于手动的回收对象


      public void Dispose()
        {
            _isDisposed = true;

            DisposeItem(_firstItem);
            _firstItem = null;

            ObjectWrapper[] items = _items;
            for (var i = 0; i < items.Length; i++)
            {
                DisposeItem(items[i].Element);
                items[i].Element = null;
            }
        }

        private void DisposeItem(T item)
        {
            if (item is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }


LeakTrackingObjectPool

LeakTrackingObjectPool 实现了 ObjectPool,它定义了 ConditionalWeakTable 他是一个弱引用字典,ConditionalWeakTable<TKey,TValue> 中的所有 Key 和所有的 Value 都是弱引用的,并且会在其 Key 被回收或者 Key 和 Value 都被回收之后自动从集合中消失。这意味着当你使用它来为一个类型附加一些字段或者属性的时候完全不用担心内存泄漏的问题


    public class LeakTrackingObjectPool<T> : ObjectPool<T> where T : class
    {
        private readonly ConditionalWeakTable<T, Tracker> _trackers = new ConditionalWeakTable<T, Tracker>();
        private readonly ObjectPool<T> _inner;

        public LeakTrackingObjectPool(ObjectPool<T> inner)
        {
            if (inner == null)
            {
                throw new ArgumentNullException(nameof(inner));
            }

            _inner = inner;
        }

        public override T Get()
        {
            var value = _inner.Get();
            _trackers.Add(value, new Tracker());
            return value;
        }

        public override void Return(T obj)
        {
            Tracker tracker;
            if (_trackers.TryGetValue(obj, out tracker))
            {
                _trackers.Remove(obj);
                tracker.Dispose();
            }

            _inner.Return(obj);
        }

        private class Tracker : IDisposable
        {
            private readonly string _stack;
            private bool _disposed;

            public Tracker()
            {
                _stack = Environment.StackTrace;
            }

            public void Dispose()
            {
                _disposed = true;
                GC.SuppressFinalize(this);
            }

            ~Tracker()
            {
                if (!_disposed && !Environment.HasShutdownStarted)
                {
                    Debug.Fail($"{typeof(T).Name} was leaked. Created at: {Environment.NewLine}{_stack}");
                }
            }
        }
    }

参考

https://blog.walterlv.com/post/conditional-weak-table.html

https://www.cnblogs.com/edison0621/p/11747912.html

原文出处:https://www.cnblogs.com/yyfh/p/11980645.html

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

.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

.NET Core 3.0之深入源码理解ObjectPool(二)

.NET Core 3.0之深入源码理解ObjectPool(二)

写在前面

前文主要介绍了ObjectPool的一些理论基础,本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图:

objectpool2

核心组件

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:  }

实现机制

其内部实现逻辑较为简单,充分考虑到了一般实现、对象追踪、对象释放等场景的使用方式。

以下为其逻辑图:
objectpool3

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:  }

原文出处:https://www.cnblogs.com/edison0621/p/11747912.html

.NET Core 中对象池 Object Pool的使用

.NET Core 中对象池 Object Pool的使用

一、什么是对象池

对象池简单来说就是一种为对象提供可复用能力的软件设计思路。我们常说有借有还,再借不难,而对象池就是通过借和还这样两个动作来保证对象可以被重复使用,从而节省频繁创建对象的性能开销。对象池最常用的场景是游戏设计,因为在游戏中大量存在着可复用的对象,源源不断的子弹出现并不是循环再生的。在数据库中存在着被称为连接池的东西,每当出现数据库无法连接的情况时,经验丰富的开发人员往往会先检查连接池是否满了,这其实就是对象池模式在特定领域的具体实现。因此对象池本质上就是负责一组对象创建和销毁的容器。 对象池最大的优势是可以自主地管理池子内的每个对象,决定它们是需要被回收还是可以重复使用。我们都知道创建一个新对象需要消耗一定的系统资源,一旦这些对象可以重复地使用就可以节省系统资源开销,这对提高系统性能会非常有帮助。下面的代码实微软官方文档实现的一个简单的对象池:


public class ObjectPool<T> : IObjectPool<T>

{

	private Func<T> _instanceFactory;

	private ConcurrentBag<T> _instanceItems;

	public ObjectPool(Func<T> instanceFactory)

	{

		_instanceFactory = instanceFactory ?? 

		throw new ArgumentNullException(nameof(instanceFactory));

		_instanceItems = new ConcurrentBag<T>();

	}

	public T Get()

	{

		T item;

		if (_instanceItems.TryTake(out item)) return item;

		return _instanceFactory();

	}

	public void Return(T item)

	{

		_instanceItems.Add(item);

	}

}

二、.NET Core 中的对象池

.NET Core 中微软已经为我们提供了对象池的实现,即Microsoft.Extensions.ObjectPool。它主要提供了三个核心的组件,分别是ObjectPoolObjectPoolProviderIPooledObjectPolicyObjectPool是一个抽象类,对外提供了Get和Return两个方法,这就是所谓的有借有还。ObjectPoolProvider同样是一个抽象类,它的职责就是创建ObjectPool,它提供了两个Create方法,两者的区别是无参数版本本质上使用的是DefaultPooledObjectPolicy。它和DefaultObjectPool、DefaultObjectPoolProvider都是微软提供的默认实现,IPooledObjectPolicy可以为不同的对象池定义不同的策略,来决定对象如何借、是否可以还。DefaultObjectPool内部使用ObjectWrapper[]来管理对象,ObjectWrapper[]的大小等于 maximumRetained-1,默认情况下maximumRetained等于Environment.ProcessorCount * 2,这里主要用到了Interlocked.CompareExchange()方法,

具体代码如下:

public override T Get()

{

  var item = _firstItem;

  if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)

  {

    var items = _items;

    for (var i = 0; i < items.Length; i++)

    {

      item = items[i].Element;

      if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)

      {

        return item;

      }

    }

    item = Create();

  }

  return item;

}

// Non-inline to improve its code quality as uncommon path

[MethodImpl(MethodImplOptions.NoInlining)]

private T Create() => _fastPolicy?.Create() ?? _policy.Create();



public override void Return(T obj)

{

  if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))

  {

    if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)

    {

      var items = _items;

      for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)

      {

      }

    }

  }

}

这里用到Interlocked.CompareExchange()方法,Get()方法将items[i].Elementnull进行交换,将指定元素设为 null 并返回原始值。Return()方法将items[i].Element和obj交换后的值不为 null,表示指定元素已经归还,这个方法只有在第一个参数和第三个参数相等时才会发生交换。

说了这么多,我们来看一下对象池具体的用法:

var service = new ServiceCollection();

//使用DefaultObjectPoolProvider

service.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

//使用默认策略

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create<Foo>();

});

//使用自定义策略

service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>

{

  var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();

  return objectPoolProvider.Create(new FooObjectPoolPolicy());

});



var serviceProvider = _service.BuildServiceProvider();



var objectPool = _serviceProvider.GetService<ObjectPool<Foo>>();



//有借有还,两次是同一个对象

var item1 = objectPool.Get();

objectPool.Return(item1);

var item2 = objectPool.Get();

Assert.AreEqual(item1, item2);//true



//有借无还,两次是不同的对象

var item3 = objectPool.Get();

var item4 = objectPool.Get();

Assert.AreEqual(item3, item4);//false

上面的代码中Foo和FooObjectPoolPolicy是两个工具类:

public class Foo

{

  public string Id { get; set; }

  public DateTime? CreatedAt { get; set; }

  public string CreatedBy { get; set; }

}



public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>

{

  public Foo Create()

  {

    return new Foo()

    {

      Id = Guid.NewGuid().ToString("N"),

      CreatedAt = DateTime.Now,

      CreatedBy = "zs"

    };

  }



  public bool Return(Foo obj)

  {

    return true;

  }

}

TIP:当你需要控制对象池内的对象如何被创建的时候,你可以考虑实现自定义的IPooledObjectPolicy<T>,反之DefaultPooledObjectPolicy<T>实现完全可以满足你的使用。

三、本文小结

实现对象池可以考虑ConcurrentBag、Stack、Queue以及BlockingCollection等多种数据结构,而微软在.NET Core 中已经为我们实现了一个简单的对象池,大多数情况下,我们只需要定义自己的IPooledObjectPolicy去决定对象应该怎么样借、怎么样还。总之游戏世界里的 GameObject、数据库里的连接池,都是对象池模式在各自领域中的具体实现。

TIP:对象池是一种通过复用对象来减少资源开销进而实现提高系统性能的软件设计模式,其核心是控制容器内对象的生命周期来规避系统的主动回收,从对象池中借出的对象必须要及时归还,否则会造成对象池中没有可用资源。

到此这篇关于 .NET Core 中对象池 Object Pool的使用的文章就介绍到这了,更多相关 .NET Core 中对象池 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

您可能感兴趣的文章:
  • .NET Core对象池的应用:扩展篇
  • .NET Core对象池的应用:设计篇
  • .NET Core对象池的应用:编程篇
  • .NET Core中如何实现或使用对象池?
  • ASP.NET Core中的对象池介绍

.NET Core中Object Pool的多种用法详解

.NET Core中Object Pool的多种用法详解

前言

复用,是一个重要的话题,也是我们日常开发中经常遇到的,不可避免的问题。

举个最为简单,大家最为熟悉的例子,数据库连接池,就是复用数据库连接。

那么复用的意义在那里呢?

简单来说就是减少不必要的资源损耗。

除了数据库连接,可能在不同的情景或需求下,还会有很多其他对象需要进行复用,这个时候就会有所谓的 Object Pool(对象池)。

小伙伴们应该也自己实现过类似的功能,或用ConcurrentBag,或用ConcurrentQueue,或用其他方案。

这也里分享一个在微软文档中的实现

How to: Create an Object Pool by Using a ConcurrentBag

当然,在.NET Core中,微软已经帮我们实现了一个简单的Object Pool。

我们只需要添加Microsoft.Extensions.ObjectPool的引用即可使用了。

Microsoft.Extensions.ObjectPool

Microsoft.Extensions.ObjectPool可以说是.NET Core的一个基础类库。

它位于aspnet的Common项目中,类型其他基础模块都有使用相关的功能,也好比Routing项目。

下面就简单看看它的用法。

在开始之前,我们先定义一个可以复用的object

public class Demo
{
 public int Id { get; set; }
 public string Name { get; set; }
 public DateTime CreateTimte { get; set; }
}

用法1

var defalutPolicy = new DefaultPooledObjectPolicy<Demo>();
//最大可保留对象数量 = Environment.ProcessorCount * 2
var defaultPool = new DefaultObjectPool<Demo>(defalutPolicy);
for (int i = 0; i < PoolSize; i++)
{
 item1 = defaultPool.Get();
 Console.WriteLine($"#{i+1}-{item1.Id}-{item1.Name}-{item1.CreateTimte}");
}

在创建pool之前,我们要先定义一个Policy。这里直接用自带的DefaultPooledObjectPolicy来构造。

对象池会有一个维护的最大数量,线程数。

通过pool对象的Get方法,从对象池中取出一个对象。

上面代码运行结果

#1-0--01/01/0001 00:00:00
#2-0--01/01/0001 00:00:00
#3-0--01/01/0001 00:00:00
#4-0--01/01/0001 00:00:00
#5-0--01/01/0001 00:00:00
#6-0--01/01/0001 00:00:00
#7-0--01/01/0001 00:00:00
#8-0--01/01/0001 00:00:00

这个结果说明,Object Pool 中的对象都是直接new出来的,并没有对一些属性进行贬值操作,这个时候往往没有太多实际意义。

因为DefaultPooledObjectPolicy本来就是直接new了一个对象出来,很多时候,这并不是我们所期望的!

要想符合我们实际的使用,就要自己定义一个Policy!

下面来看看用法2

用法2

先定义一个Policy,实现 IPooledObjectPolicy 这个接口。T很自然就是我们的Demo类了。

public class DemoPooledObjectPolicy : IPooledObjectPolicy<Demo>
{
 public Demo Create()
 {
  return new Demo { Id = 1, Name = "catcher", CreateTimte = DateTime.Now };
 }
 
 public bool Return(Demo obj)
 {
  return true;
 }
}

这里要实现Create和Return两个方法。

见名知义,Create方法就是用来创建Demo对象的,Return方法就是将Demo对象扔回Object Pool的(有借有还)。

然后是用DemoPooledObjectPolicy去替换DefaultPooledObjectPolicy。

var demoPolicy = new DemoPooledObjectPolicy();
var defaultPoolWithDemoPolicy = new DefaultObjectPool<Demo>(demoPolicy,1);
//借
item1 = defaultPoolWithDemoPolicy.Get();
//还
defaultPoolWithDemoPolicy.Return(item1);
//借,但是不还
item2 = defaultPoolWithDemoPolicy.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
//创建一个新的
item3 = defaultPoolWithDemoPolicy.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item1);

这里定义了对象池只保留一个对象。

由于从object pool中取出来之后,有一步还回去的操作,所以item1和item2应当是同一个对象。

从object pool中拿出了item2之后,它并没有还回去,所以object pool会基于我们定义的Policy去创建一个新的对象出来。

下面是用法2的输出结果:

1-catcher-09/17/2018 22:32:38
1-catcher-09/17/2018 22:32:38
True
1-catcher-09/17/2018 22:32:38
False

可以看到item1,item2和item3的各个属性是一样的,并且item1和item2确实是同一个对象。item3和item1并不是同一个。

用法3

除了DefaultObjectPool外,还有DefaultObjectPoolProvider也可以创建一个Object Pool。

创建一个Object Pool,一定是离不开Policy的,所以这里还是用了我们自己定义的DemoPooledObjectPolicy。

var defaultProvider = new DefaultObjectPoolProvider();
var policy = new DemoPooledObjectPolicy();
//default maximumRetained is Environment.ProcessorCount * 2
ObjectPool<Demo> pool = defaultProvider.Create(policy);
item1 = pool.Get();
pool.Return(item1);
item2 = pool.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
item3 = pool.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item2);

用Provider创建Object Pool时,不能指定保留的最大对象数量,只能用的是默认的Environment.ProcessorCount * 2。

后面的使用,和用法2是一样的。

可以看到item1和item2是同一个对象。从Object Pool中取对象的时候,会取第一个,所以还回去后,再取的话,还是会取到原来的第一个。

item3那里是直接从Object Pool中取出来的,没有再次创建,因为这里的Object Pool维护着多个对象,而不是用法2中的只有一个,所以它是直接从Pool中拿的。

下面是输出结果

1-catcher-09/17/2018 22:38:34
1-catcher-09/17/2018 22:38:34
True
1-catcher-09/17/2018 22:38:34
False

和用法2,本质是一样的。

用法4

好像上面的用法,都不那么像我们正常使用的。我们还是需要依赖注入的。

那么我们最后就来看看怎么结合依赖注入吧。当然这里的本质还是离不开Policy和Provider这两个东西。

IServiceCollection services = new ServiceCollection();
services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
services.AddSingleton(s =>
{
 var provider = s.GetRequiredService<ObjectPoolProvider>();
 return provider.Create(new DemoPooledObjectPolicy());
});
ServiceProvider serviceProvider = services.BuildServiceProvider();
var pool = serviceProvider.GetService<ObjectPool<Demo>>();
item1 = pool.Get();
pool.Return(item1);
item2 = pool.Get();
Console.WriteLine($"{item1.Id}-{item1.Name}-{item1.CreateTimte}");
Console.WriteLine($"{item2.Id}-{item2.Name}-{item2.CreateTimte}");
Console.WriteLine(item1 == item2);
item3 = pool.Get();
Console.WriteLine($"{item3.Id}-{item3.Name}-{item3.CreateTimte}");
Console.WriteLine(item3 == item2);

我们首先需要完成对Provider的注册,然后直接拿它的实例去创建一个Object Pool即可。

如果想在其他地方用,通过构造函数注入即可。

这里的结果也是和前面一样的,没什么好多说的。

总结

在这几种用法中,我们最常用的应该是用法4。

但是无论那种用法,我们都需要了解,Object Pool离不开Pool,Policy和Provider这三个家伙。

有了这三个,或许我们就可以为所欲为了。

当然,它还提供了几个特殊的东西,有兴趣的可以去看看。

•LeakTrackingObjectPool

•StringBuilderPooledObjectPolicy

最后用一个脑图结束本文。

以上所述是小编给大家介绍的.NET Core中Object Pool的多种用法详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

您可能感兴趣的文章:
  • asp.net core webapi项目配置全局路由的方法示例
  • Asp.Net Core中基于Session的身份验证的实现
  • .net core 读取本地指定目录下的文件的实例代码
  • .NET Core中使用HttpClient的正确姿势
  • .Net Core中使用Quartz.Net实践记录
  • .Net Core中间件之静态文件(StaticFiles)示例详解
  • 详解.NET Core中的数据保护组件

今天关于ASP.NET Core 中的 ObjectPool 对象重用的分享就到这里,希望大家有所收获,若想了解更多关于.NET Core 3.0之深入源码理解ObjectPool(一)、.NET Core 3.0之深入源码理解ObjectPool(二)、.NET Core 中对象池 Object Pool的使用、.NET Core中Object Pool的多种用法详解等相关知识,可以在本站进行查询。

本文标签: