• .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
  • 相关阅读:
    【miscellaneous】海康威视监控摄像头实现web端无插件监控实拍效果
    【miscellaneous】海康威视监控摄像头实现web端无插件监控实拍效果
    【miscellaneous】单播、广播和多播IP地址
    【miscellaneous】单播、广播和多播IP地址
    【miscellaneous】IP多播技术及其编程
    【miscellaneous】IP多播技术及其编程
    【miscellaneous】多播的实现和需要注意的问题
    【miscellaneous】多播的实现和需要注意的问题
    【VS开发】组播(多播)的C程序实战
    【VS开发】组播(多播)的C程序实战
  • 原文地址:https://www.cnblogs.com/edison0621/p/11669000.html
Copyright © 2020-2023  润新知