.NET对象池的使用
对象池是什么
对象池就是对象的容器,旨在优化资源的使用,通过在一个容器中池化对象,并根据需要重复使用这些池化对象来满足性能上的需求
对象池的应用场景
当一个对象被激活时,便被从池中取出。当对象被停用时,它又被放回池中,等待下一个请求。对象池一般用于对象的初始化过程代价较大或使用频率较高的场景
如何实现
在 ASP.NET Core 框架里已经内置了一个对象池功能的实现:
Microsoft.Extensions.ObjectPool
。如果是控制台应用程序,可以单独安装这个扩展库。
1、池化策略
- 首先,要使用 ObjectPool,需要创建一个池化策略,告诉对象池你将如何创建对象,以及如何归还对象。
- 该策略通过实现接口 IPooledObjectPolicy 来定义
// 一个最简单的策略实现:
public class FooPooledObjectPolicy : IPooledObjectPolicy<Foo>
{
public Foo Create()
{
return new Foo();
}
public bool Return(Foo obj)
{
return true;
}
}
如果每次编码都要定义这样的策略,会比较麻烦,可以自己定义一个通用的泛型实现。
Microsoft.Extensions.ObjectPool
中也提供了一个默认的泛型实现:DefaultPooledObjectPolicy
。如果不需要定义复杂的构造逻辑,使用默认的就行
2、对象池的使用
- 对象池使用的原则是:有借有还,再借不难。
当对象池中没有实例时,则创建实例并返回给调用组件;当对象池中已有实例时,则直接取一个现有实例返回给调用组件。而且这个过程是线程安全的。
Microsoft.Extensions.ObjectPool
提供了默认的对象池实现:DefaultObjectPool
,它提供了借Get
和还Return
操作接口。创建对象池时需要提供池化策略IPooledObjectPolicy
作为其构造参数。var policy = new DefaultPooledObjectPolicy(); var pool = new DefaultObjectPool(policy);
using Microsoft.Extensions.ObjectPool;
using System;
var policy = new DefaultPooledObjectPolicy();
var pool = new DefaultObjectPool(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
是同一样对象。我们再来试试只借不还会是什么样子 -
可以看到,两个对象是不同的实例。所以,当调用组件从对象池中借走一个对象实例,使用完后应立即归还给对象池,以便重复使用,避免因构造新对象消耗过多资源
3、指定对象池容量
在创建 DefaultObjectPool 时,还可以指定第二个参数:对象池的容量。它表示最大可从该对象池取出的对象数量,指定数量以外的被取走的对象将不会被池化
using Microsoft.Extensions.ObjectPool;
using System;
var policy = new DefaultPooledObjectPolicy();
// 指定容量为 2。
var pool = new DefaultObjectPool(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 个对象,第三个对象不会被池化
4、在 ASP.NET Core 中使用
- ASP.NET Core 框架内置好了 Microsoft.Extensions.ObjectPool,不需要单独安装
- 官方示例
// 先定义一个中间件
public class BirthdayMiddleware
{
private readonly RequestDelegate _next;
public BirthdayMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, ObjectPoolbuilderPool)
{
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();
services.TryAddSingleton<objectpool>(serviceProvider =>
{
var provider = serviceProvider.GetRequiredService();
var policy = new StringBuilderPooledObjectPolicy();
return provider.Create(policy);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware();
}
}
DefaultObjectPoolProvider
,它是默认的对象池Provider
,所以你也可以自定义自己的对象池Provider
归纳
Microsoft.Extensions.ObjectPool
提供的对象池功能还是挺灵活的。普通场景使用使用默认的池化策略、默认的对象池和默认的对象池提供者就可以满足需求,也可以自定义其中任意某部件来实现比较特殊或复杂的需求。
对象池的使用原则是:有借有还,再借不难。当调用组件从对象池中借走一个对象实例,使用完后应立即归还给对象池,以便重复利用,避免因过多的对象初始化影响系统性能
相关文章
- 【ASP.NET】基本对象
- 使用 Visual Studio 部署 .NET Core 应用 ——.Net Core 部署到SUSE Linux Enterprise Server 12 SP2 64 位(GNOME 版本3.20.2)
- 【ASP.NET】基本对象
- .NET Core开源Quartz.Net作业调度框架实战演练
- C#.NET 如何快速输入一个对象事件对应的方法
- ASP.NET缺少程序集引用怎么办
- 这才是你需要的最基础的.Net基础面试题(通俗易懂,最基础的.Net)
- asp.net字符串分割函数用法
- .NET/C#中对自定义对象集合进行自定义排序的方法
- .NET多线程执行函数
- SQLServer · 最佳实践 · 开发基于.NET CORE的LINUX版本的数据库应用
- MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题
- C#[Serializable]在C#中的作用-NET 中的对象序列化
- 重新整理 .net core 实践篇—————Mediator实践[二十八]
- 对象池在 .NET (Core)中的应用[1]: 编程篇
- 200行代码,7个对象——让你了解ASP.NET Core框架的本质[3.x版]
- [ASP.NET Core 3框架揭秘] 配置[4]:将配置绑定为对象
- ASP.NET Core的配置(3): 将配置绑定为对象[上篇]
- .Net 垃圾回收机制原理(一)
- .NET平台开源项目速览(2)Compare .NET Objects对象比较组件
- TeeChart for .NET 2023.4.13 Crack
- .Net——使用.net内置处理程序处理自己定义节点Demo
- VB.NET版机房收费系统---报表
- lotus-local-net 2k v1.17.0-rc4
- VB.net:VB.net编程语言学习之基于VS软件连接SQL Server(利用ADO.NET操作数据库/添加新数据源/DataGridView数据表格控件)的简介、案例应用之详细攻略