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

 更新时间:2021年11月22日 12:52:32   作者:494324190  
这篇文章主要介绍了 .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 中对象池 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • ASP.NET Core使用功能开关控制路由访问操作(续)

    ASP.NET Core使用功能开关控制路由访问操作(续)

    这篇文章主要介绍了ASP.NET Core使用功能开关控制路由访问操作的(续),上一篇文章我们已经介绍过一部份该相关内容,​​在本文,我们可以判断当前路由地址是否为调试地址,让评估返回真,需要的小伙伴可以参考一下
    2022-02-02
  • .Net 6中的PeriodTimer介绍

    .Net 6中的PeriodTimer介绍

    这篇文章主要介绍了.Net 6中的PeriodTimer,.net 6中新增了一个异步计时器PeroidTimer,相对普通Timer的回调, 它的模型更简单,下面一起来看看具体详情吧
    2022-01-01
  • .net中线程同步的典型场景和问题剖析

    .net中线程同步的典型场景和问题剖析

    在使用多线程进行编程时,有一些经典的线程同步问题,对于这些问题,.net提供了多种不同的类来解决
    2012-11-11
  • ASP.NET webUploader上传大视频文件相关web.config配置

    ASP.NET webUploader上传大视频文件相关web.config配置

    本文主要介绍了webUploader上传大视频文件相关web.config的配置。具有一定的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • .Net Framework .Net  .NET Standard的概念及区别

    .Net Framework .Net .NET Standard的概念及区别

    这篇文章主要介绍了.Net Framework .Net .NET Standard的概念及区别,需要的朋友可以参考下
    2021-08-08
  • asp.net 获取数据库连接字符串

    asp.net 获取数据库连接字符串

    本文主要介绍了asp.net获取数据库连接字符串的具体实现代码,具有一定参考价值,需要的朋友可以看下
    2016-12-12
  • .Net性能调优-ArrayPool详情

    .Net性能调优-ArrayPool详情

    ArrayPool具有高性能 托管 数组缓冲池,可重复使用,用 租用 空间的方式代替 重新分配 数组空间的行为的特点及可以在频繁创建和销毁数组的情况下 提高性能 ,减少垃圾回收器的压力的优点,下面文章内容将详细对其做介绍,需要的朋友可以参考一下
    2021-09-09
  • 认识ASP.NET配置文件Web.config

    认识ASP.NET配置文件Web.config

    Web.config文件是一个XML文本文件,它用来储存 ASP.NET Web 应用程序的配置信息(如最常用的设置ASP.NET Web 应用程序的身份验证方式),它可以出现在应用程序的每一个目录中
    2006-07-07
  • .NET 6 即将到来的新特性  隐式命名空间引用

    .NET 6 即将到来的新特性 隐式命名空间引用

    ASP.NET 现在我们还是需要手动加命名空间引用,在以后的版本中可能就不需要手动加命名空间的引用了,本文就来介绍.NET 6即将到来的新特性--隐式命名空间引用,,需要的朋友可以参考下面文章内容
    2021-09-09
  • .NET6新特性之 隐式命名空间引用

    .NET6新特性之 隐式命名空间引用

    本文给大家分享的是 .NET6特新 隐式命名空间引用,如果我们要在新加一个命名空间的引用,可以在项目文件中配置增加<Using Include="命名空间"/>,如果需要移除一个命名空间可以这么做<Using Remove="命名空间"/>,下面来看看文章详细介绍内容吧,需要的朋友可以参考一下
    2021-11-11

最新评论