zoukankan      html  css  js  c++  java
  • .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
  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/edison0621/p/11669000.html
Copyright © 2011-2022 走看看