- 全球化与本土化
- 性能
- 高级
使用 ASP.NET Core 中的 ObjectPool 对象重用
通过Steve Gordon, Ryan Nowak,和Rick Anderson
Microsoft.Extensions.ObjectPool 是支持将一组对象保存在内存中以供重复使用,而不允许的对象进行垃圾收集的 ASP.NET Core 基础结构的一部分。
您可能想要使用对象池,如果正在管理的对象:
- 成本分配/初始化。
- 表示一些有限的资源。
- 使用以可预测方式和频率。
例如,ASP.NET Core 框架将使用对象池在某些位置重复使用StringBuilder实例。 StringBuilder
分配和管理其自己的缓冲区来存放字符数据。 ASP.NET Core 定期使用StringBuilder
为了实现功能,并重复使用它们提供性能优势。
对象池始终不会提高性能:
- 对象的初始化成本较高,除非它是通常较慢,可从池中获取的对象。
- 池管理的对象不解除分配,直到该池将被释放。
使用对象池仅在为应用或库使用实际的方案的性能数据收集后。
警告:ObjectPool
不会实现IDisposable
。我们不建议将用于需要可供使用的类型。
注意:ObjectPool 不置于将分配的对象数的限制,它将保留的对象数施加限制。
概念
ObjectPool<T> -基本对象池抽象。 用于获取和返回对象。
PooledObjectPolicy<T> -实现此接口可以自定义如何创建对象以及如何重置时返回到池中。 这可以传递到直接构造的对象池...或
Create 充当用于创建对象池的工厂。
ObjectPool 可以采用多种方式应用:
- 实例化一个池。
- 注册中的池依赖关系注入(DI) 为一个实例。
- 注册
ObjectPoolProvider<>
在 DI 并将其用作工厂。
如何使用 ObjectPool
调用ObjectPool<T>来获取的对象和Return来返回的对象。 没有任何要求返回的每个对象。 如果不返回一个对象,它将进行垃圾回收。
ObjectPool 示例
下面的代码:
- 将添加
ObjectPoolProvider
到依赖关系注入(DI) 容器。 - 添加并配置
ObjectPool<StringBuilder>
到 DI 容器。 - 添加
BirthdayMiddleware
。
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) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // Test using /?firstname=Steve&lastName=Gordon&day=28&month=9 app.UseMiddleware<BirthdayMiddleware>(); } }
下面的代码实现 BirthdayMiddleware
public class BirthdayMiddleware { private readonly RequestDelegate _next; public BirthdayMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, ObjectPool<StringBuilder> builderPool) { if (context.Request.Query.TryGetValue("firstName", out var firstName) && context.Request.Query.TryGetValue("lastName", out var lastName) && context.Request.Query.TryGetValue("month", out var month) && context.Request.Query.TryGetValue("day", out var day) && int.TryParse(month, out var monthOfYear) && int.TryParse(day, out var dayOfMonth)) { var now = DateTime.UtcNow; // Ignoring timezones. // Request a StringBuilder from the pool. var stringBuilder = builderPool.Get(); try { stringBuilder.Append("Hi ") .Append(firstName).Append(" ").Append(lastName).Append(". "); if (now.Day == dayOfMonth && now.Month == monthOfYear) { stringBuilder.Append("Happy birthday!!!"); await context.Response.WriteAsync(stringBuilder.ToString()); } else { var thisYearsBirthday = new DateTime(now.Year, monthOfYear, dayOfMonth); int daysUntilBirthday = thisYearsBirthday > now ? (thisYearsBirthday - now).Days : (thisYearsBirthday.AddYears(1) - now).Days; stringBuilder.Append("There are ") .Append(daysUntilBirthday).Append(" days until your birthday!"); await context.Response.WriteAsync(stringBuilder.ToString()); } } finally // Ensure this runs even if the main code throws. { // Return the StringBuilder to the pool. builderPool.Return(stringBuilder); } return; } await _next(context); } }