.NET Core中如何实现或使用对象池?

 更新时间:2021年07月12日 11:44:09   作者:精致码农  
什么是对象池?简单来说它就是一种为对象提供可复用性能力的软件设计思路,对象池就是通过“借”和“还”这样两个动作来保证对象可以被重复使用,这篇文章主要给大家介绍了关于.NET Core中如何实现或使用对象池的相关资料,需要的朋友可以参考下

前言

池这个概念大家都很熟悉,比如我们经常听到数据库连接池和线程池。它是一种基于使用预先分配资源集合的性能优化思想。

简单说,对象池就是对象的容器,旨在优化资源的使用,通过在一个容器中池化对象,并根据需要重复使用这些池化对象来满足性能上的需求。当一个对象被激活时,便被从池中取出。当对象被停用时,它又被放回池中,等待下一个请求。对象池一般用于对象的初始化过程代价较大或使用频率较高的场景。

那在 .NET 中如何实现或使用对象池呢?

在 ASP.NET Core 框架里已经内置了一个对象池功能的实现:Microsoft.Extensions.ObjectPool。如果是控制台应用程序,可以单独安装这个扩展库。

池化策略

首先,要使用 ObjectPool,需要创建一个池化策略,告诉对象池你将如何创建对象,以及如何归还对象。

该策略通过实现接口 IPooledObjectPolicy 来定义,下面是一个最简单的策略实现:

public class FooPooledObjectPolicy : IPooledObjectPolicy<Foo>
{
    public Foo Create()
    {
        return new Foo();
    }

    public bool Return(Foo obj)
    {
        return true;
    }
}

如果每次编码都要定义这样的策略,会比较麻烦,可以自己定义一个通用的泛型实现。Microsoft.Extensions.ObjectPool 中也提供了一个默认的泛型实现:DefaultPooledObjectPolicy<T>。如果不需要定义复杂的构造逻辑,使用默认的就行。下面我们来看看怎么使用。

对象池的使用

对象池使用的原则是:有借有还,再借不难。

当对象池中没有实例时,则创建实例并返回给调用组件;当对象池中已有实例时,则直接取一个现有实例返回给调用组件。而且这个过程是线程安全的。

Microsoft.Extensions.ObjectPool 提供了默认的对象池实现:DefaultObjectPool<T>,它提供了借 Get 和还 Return 操作接口。创建对象池时需要提供池化策略 IPooledObjectPolicy<T> 作为其构造参数。

var policy = new DefaultPooledObjectPolicy<Foo>();
var pool = new DefaultObjectPool<Foo>(policy);

我们来看一个常规示例(C# 9.0 单文件完整代码):

using Microsoft.Extensions.ObjectPool;
using System;

var policy = new DefaultPooledObjectPolicy<Foo>();
var pool = new DefaultObjectPool<Foo>(policy);

// 借
var item1 = pool.Get();
// 还
pool.Return(item1);
Console.WriteLine("item 1: {0}", item1.Id);

// 借
var item2 = pool.Get();
// 还
pool.Return(item2);
Console.WriteLine("item 2: {0}", item2.Id);

Console.ReadKey();

public class Foo
{
    public string Id { get; set; } = Guid.NewGuid().ToString("N");
}

打印结果:

通过打印的 Id 知道,item1 和 item2 是同一样对象。

我们再来看看只借不还会是什么样子:

// ...

// 借
var item1 = pool.Get();
Console.WriteLine("item 1: {0}", item1.Id);

// 再借
var item2 = pool.Get();
Console.WriteLine("item 2: {0}", item2.Id);

// ...

打印结果:

可以看到,两个对象是不同的实例。所以,当调用组件从对象池中借走一个对象实例,使用完后应立即归还给对象池,以便重复使用,避免因构造新对象消耗过多资源。

指定对象池容量

在创建 DefaultObjectPool<T> 时,还可以指定第二个参数:对象池的容量。它表示最大可从该对象池取出的对象数量,指定数量以外的被取走的对象将不会被池化。我来演示一下,大家就知道什么意思了,请看示例:

using Microsoft.Extensions.ObjectPool;
using System;

var policy = new DefaultPooledObjectPolicy<Foo>();

// 指定容量为 2。
var pool = new DefaultObjectPool<Foo>(policy, 2);

// 借走 3 个
var item1 = pool.Get();
Console.WriteLine("item 1: {0}", item1.Id);
var item2 = pool.Get();
Console.WriteLine("item 2: {0}", item2.Id);
var item3 = pool.Get();
Console.WriteLine("item 3: {0}", item3.Id);

// 再还会 3 个
pool.Return(item1);
pool.Return(item2);
pool.Return(item3);


// 再借走 3 个
var item4 = pool.Get();
Console.WriteLine("item 4: {0}", item4.Id);
var item5 = pool.Get();
Console.WriteLine("item 5: {0}", item5.Id);
var item6 = pool.Get();
Console.WriteLine("item 6: {0}", item6.Id);

Console.ReadKey();

注意示例代码中我给对象池指定了容量为 2,然后借走 3 个再归还 3 个,后面再借走 3 个。来看看打印结果:

我们看到,item1 与 item4 是同一个对象,item2 与 item5 是同一个对象。item3 与 item6 却不是同一个对象。

也就是说,当对象从池中取出超过指定容量的对象数量,虽然归还了相同数量的对象,但对象池只允许容纳 2 个对象,第三个对象不会被池化。

在 ASP.NET Core 中使用

ASP.NET Core 框架内置好了 Microsoft.Extensions.ObjectPool,不需要单独安装。官方文档有个基于 ASP.NET Core 的使用示例:

https://docs.microsoft.com/en-us/aspnet/core/performance/objectpool

这个例子把 StringBuilder 做了池化。我这里就直接贴官方的例子了,为了更直观些,我把无关的代码简化掉了。

先定义一个中间件:

public class BirthdayMiddleware
{
    private readonly RequestDelegate _next;

    public BirthdayMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, ObjectPool<StringBuilder> builderPool)
    {
        var stringBuilder = builderPool.Get();
        try
        {
            stringBuilder.Append("Hi");
            // 其它处理
            await context.Response.WriteAsync(stringBuilder.ToString());
        }
        finally // 即使出错也要保证归还对象
        {
            builderPool.Return(stringBuilder);
        }
    }
}

在 Startup 中注册相应的服务和中间件:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();

        services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
        {
            var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
            var policy = new StringBuilderPooledObjectPolicy();
            return provider.Create(policy);
        });
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMiddleware<BirthdayMiddleware>();
    }
}

这个示例用了 DefaultObjectPoolProvider,它是默认的对象池 Provider,所以你也可以自定义自己的对象池 Provider。

总结

Microsoft.Extensions.ObjectPool 提供的对象池功能还是挺灵活的。普通场景使用使用默认的池化策略、默认的对象池和默认的对象池提供者就可以满足需求,也可以自定义其中任意某部件来实现比较特殊或复杂的需求。

对象池的使用原则是:有借有还,再借不难。当调用组件从对象池中借走一个对象实例,使用完后应立即归还给对象池,以便重复利用,避免因过多的对象初始化影响系统性能。

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

相关文章

  • 详解ASP.NET MVC 下拉框的传值的两种方式

    详解ASP.NET MVC 下拉框的传值的两种方式

    这篇文章主要介绍了详解ASP.NET MVC 下拉框的传值的两种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • ASP.NET MVC使用母版页视图

    ASP.NET MVC使用母版页视图

    这篇文章介绍了ASP.NET MVC使用母版页视图的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • asp.net画曲线图(折线图)代码 详细注释

    asp.net画曲线图(折线图)代码 详细注释

    asp.net画曲线图(折线图), 需要的朋友可以参考下。
    2010-03-03
  • sql server中批量插入与更新两种解决方案分享(asp.net)

    sql server中批量插入与更新两种解决方案分享(asp.net)

    xml和表值函数的相对复杂些这里简单贴一下bcp和SqlDataAdapter进行批量跟新插入方法,未经整理还望见谅
    2012-05-05
  • .net中的session与cookies区别及使用方法

    .net中的session与cookies区别及使用方法

    cookie数据存放在客户的浏览器上,session数据放在服务器上,cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session
    2013-04-04
  • Discuz!NT 3与asp.net 整合的实例教程

    Discuz!NT 3与asp.net 整合的实例教程

    本次整合只针对NETSNS中的代码做了少许修改,完成了基本的和论坛同步注册,登陆和注销,信息获取,信息修改。为的是给各位Discuz!NT API爱好者做一个简单的API事例,供大家参考。
    2009-11-11
  • .net EF Core专题:EF Core 读取数据时发生了什么?

    .net EF Core专题:EF Core 读取数据时发生了什么?

    这篇文章主要介绍了EF Core 读取数据的的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • asp.net core中如何使用cookie身份验证

    asp.net core中如何使用cookie身份验证

    这篇文章主要介绍了asp.net core中如何使用cookie身份验证的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • linq中的分组操作符

    linq中的分组操作符

    这篇文章介绍了linq中的分组操作符,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • smtp发送带附件的邮件代码分享

    smtp发送带附件的邮件代码分享

    本文给大家分享一段smtp发送带附件邮件的代码,代码非常简洁易懂,有需要的小伙伴可以参考下。
    2014-11-11

最新评论