ASP.NET Core'da ObjectPool ile nesne yeniden kullanımı
Tarafından Günther Foidl, Steve Gordon ve Samson Amaugo
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Microsoft.Extensions.ObjectPool , nesnelerin çöp toplanmasına izin vermek yerine bir grup nesnenin yeniden kullanılmak üzere bellekte tutulmasını destekleyen ASP.NET Core altyapısının bir parçasıdır. içindeki Microsoft.Extensions.ObjectPool
tüm statik ve örnek yöntemleri iş parçacığı açısından güvenlidir.
Yönetilen nesneler şunlarsa uygulamalar nesne havuzunu kullanmak isteyebilir:
- Ayırmak/başlatmak pahalıdır.
- Sınırlı bir kaynağı temsil eder.
- Tahmin edilebilir ve sık kullanılan.
Örneğin, ASP.NET Core çerçevesi örnekleri yeniden kullanmak StringBuilder için bazı yerlerde nesne havuzunu kullanır. StringBuilder
karakter verilerini tutmak için kendi arabelleklerini ayırır ve yönetir. ASP.NET Core, özellikleri uygulamak için düzenli olarak kullanır StringBuilder
ve bunları yeniden kullanmak bir performans avantajı sağlar.
Nesne havuzu her zaman performansı iyileştirmez:
- Bir nesnenin başlatma maliyeti yüksek olmadığı sürece, nesneyi havuzdan almak genellikle daha yavaştır.
- Havuz tarafından yönetilen nesneler, havuz kaldırılana kadar ayrılmaz.
Nesne havuzu oluşturmayı yalnızca uygulamanız veya kitaplığınız için gerçekçi senaryoları kullanarak performans verilerini topladıktan sonra kullanın.
NOT: ObjectPool, ayırdığı nesne sayısına bir sınır yerleştirmez, koruduğu nesne sayısına bir sınır yerleştirir.
ObjectPool kavramları
Ne zaman DefaultObjectPoolProvider kullanılır ve T
uygulanır IDisposable
:
- Havuza döndürülmeyen öğeler atılır.
- Havuz DI tarafından atıldığında havuzdaki tüm öğeler atılır.
NOT: Havuz atıldıktan sonra:
- Çağrısı
Get
birObjectDisposedException
oluşturur. - Çağrısı
Return
, verilen öğeyi atılır.
Önemli ObjectPool
türler ve arabirimler:
- ObjectPool<T> : Temel nesne havuzu soyutlaması. Nesneleri almak ve döndürmek için kullanılır.
- PooledObjectPolicy<T> : Bir nesnenin nasıl oluşturulduğunu ve havuza geri döndürülürken nasıl sıfırlanıp sıfırlanamını özelleştirmek için bunu uygulayın. Bu, doğrudan oluşturulacak bir nesne havuzuna geçirilebilir.
- IResettable : Nesne havuzuna döndürülürken nesneyi otomatik olarak sıfırlar.
ObjectPool bir uygulamada birden çok şekilde kullanılabilir:
- Havuz örneği oluşturma.
- Bağımlılık ekleme (DI) içinde bir havuzu örnek olarak kaydetme.
- 'yi
ObjectPoolProvider<>
DI'ye kaydetme ve fabrika olarak kullanma.
ObjectPool'u kullanma
Bir nesneyi almak ve Return nesneyi döndürmek için çağrısı Get yapın. Her nesneyi döndürme gereksinimi yoktur. Bir nesne döndürülmezse, çöp toplanır.
ObjectPool örneği
Aşağıdaki kod:
- Bağımlılık ekleme (DI) kapsayıcısına ekler
ObjectPoolProvider
. - Nesne havuzuna
IResettable
döndürülürken arabelleğin içeriğini otomatik olarak temizlemek için arabirimini uygular.
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.ObjectPool;
using System.Security.Cryptography;
var builder = WebApplication.CreateBuilder(args);
builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
builder.Services.TryAddSingleton<ObjectPool<ReusableBuffer>>(serviceProvider =>
{
var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
var policy = new DefaultPooledObjectPolicy<ReusableBuffer>();
return provider.Create(policy);
});
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
// return the SHA256 hash of a word
// https://localhost:7214/hash/SamsonAmaugo
app.MapGet("/hash/{name}", (string name, ObjectPool<ReusableBuffer> bufferPool) =>
{
var buffer = bufferPool.Get();
try
{
// Set the buffer data to the ASCII values of a word
for (var i = 0; i < name.Length; i++)
{
buffer.Data[i] = (byte)name[i];
}
Span<byte> hash = stackalloc byte[32];
SHA256.HashData(buffer.Data.AsSpan(0, name.Length), hash);
return "Hash: " + Convert.ToHexString(hash);
}
finally
{
// Data is automatically reset because this type implemented IResettable
bufferPool.Return(buffer);
}
});
app.Run();
public class ReusableBuffer : IResettable
{
public byte[] Data { get; } = new byte[1024 * 1024]; // 1 MB
public bool TryReset()
{
Array.Clear(Data);
return true;
}
}
NOT: Havuza alınan tür T
uygulamadığında IResettable
, nesnelerin havuza döndürülmeden önce durumunu sıfırlamak için bir özel PooledObjectPolicy<T>
kullanılabilir.
Microsoft.Extensions.ObjectPool , nesnelerin çöp toplanmasına izin vermek yerine bir grup nesnenin yeniden kullanılmak üzere bellekte tutulmasını destekleyen ASP.NET Core altyapısının bir parçasıdır. içindeki Microsoft.Extensions.ObjectPool
tüm statik ve örnek yöntemleri iş parçacığı açısından güvenlidir.
Yönetilen nesneler şunlarsa uygulamalar nesne havuzunu kullanmak isteyebilir:
- Ayırmak/başlatmak pahalıdır.
- Sınırlı bir kaynağı temsil eder.
- Tahmin edilebilir ve sık kullanılan.
Örneğin, ASP.NET Core çerçevesi örnekleri yeniden kullanmak StringBuilder için bazı yerlerde nesne havuzunu kullanır. StringBuilder
karakter verilerini tutmak için kendi arabelleklerini ayırır ve yönetir. ASP.NET Core, özellikleri uygulamak için düzenli olarak kullanır StringBuilder
ve bunları yeniden kullanmak bir performans avantajı sağlar.
Nesne havuzu her zaman performansı iyileştirmez:
- Bir nesnenin başlatma maliyeti yüksek olmadığı sürece, nesneyi havuzdan almak genellikle daha yavaştır.
- Havuz tarafından yönetilen nesneler, havuz kaldırılana kadar ayrılmaz.
Nesne havuzu oluşturmayı yalnızca uygulamanız veya kitaplığınız için gerçekçi senaryoları kullanarak performans verilerini topladıktan sonra kullanın.
NOT: ObjectPool, ayırdığı nesne sayısına bir sınır yerleştirmez, koruduğu nesne sayısına bir sınır yerleştirir.
Kavramlar
Ne zaman DefaultObjectPoolProvider kullanılır ve T
uygulanır IDisposable
:
- Havuza döndürülmeyen öğeler atılır.
- Havuz DI tarafından atıldığında havuzdaki tüm öğeler atılır.
NOT: Havuz atıldıktan sonra:
- Çağrısı
Get
birObjectDisposedException
oluşturur. - Çağrısı
Return
, verilen öğeyi atılır.
Önemli ObjectPool
türler ve arabirimler:
- ObjectPool<T> : Temel nesne havuzu soyutlaması. Nesneleri almak ve döndürmek için kullanılır.
- PooledObjectPolicy<T> : Bir nesnenin nasıl oluşturulduğunu ve havuza döndürülürken nasıl sıfırlanıp sıfırlanamını özelleştirmek için bunu uygulayın. Bu, doğrudan yapıya sahip bir nesne havuzuna geçirilebilir veya
- Create : Nesne havuzları oluşturmak için fabrika görevi görür.
- IResettable : Bir nesne havuzuna döndürildiğinde nesneyi otomatik olarak sıfırlar.
ObjectPool bir uygulamada birden çok şekilde kullanılabilir:
- Havuz örneği oluşturma.
- Bağımlılık ekleme (DI) içinde bir havuzu örnek olarak kaydetme.
- 'yi
ObjectPoolProvider<>
DI'ye kaydetme ve fabrika olarak kullanma.
ObjectPool'u kullanma
Bir nesneyi almak ve Return nesneyi döndürmek için çağrısı Get yapın. Her nesneyi döndürmeniz gerekmez. Bir nesne döndürmezseniz, bu nesne çöp olarak toplanır.
ObjectPool örneği
Aşağıdaki kod:
- Bağımlılık ekleme (DI) kapsayıcısına ekler
ObjectPoolProvider
. - DI kapsayıcısına ekler ve yapılandırır
ObjectPool<StringBuilder>
. - öğesini
BirthdayMiddleware
ekler.
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.ObjectPool;
using ObjectPoolSample;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
builder.Services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
{
var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
var policy = new Microsoft.Extensions.ObjectPool.StringBuilderPooledObjectPolicy();
return provider.Create(policy);
});
builder.Services.AddWebEncoders();
var app = builder.Build();
// Test using /?firstname=Steve&lastName=Gordon&day=28&month=9
app.UseMiddleware<BirthdayMiddleware>();
app.MapGet("/", () => "Hello World!");
app.Run();
Aşağıdaki kod şunu uygular: BirthdayMiddleware
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.Extensions.ObjectPool;
namespace ObjectPoolSample;
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(". ");
var encoder = context.RequestServices.GetRequiredService<HtmlEncoder>();
if (now.Day == dayOfMonth && now.Month == monthOfYear)
{
stringBuilder.Append("Happy birthday!!!");
var html = encoder.Encode(stringBuilder.ToString());
await context.Response.WriteAsync(html);
}
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!");
var html = encoder.Encode(stringBuilder.ToString());
await context.Response.WriteAsync(html);
}
}
finally // Ensure this runs even if the main code throws.
{
// Return the StringBuilder to the pool.
builderPool.Return(stringBuilder);
}
return;
}
await _next(context);
}
}
Microsoft.Extensions.ObjectPool , nesnelerin çöp toplanmasına izin vermek yerine bir grup nesnenin yeniden kullanılmak üzere bellekte tutulmasını destekleyen ASP.NET Core altyapısının bir parçasıdır.
Yönetilen nesneler şunlarsa nesne havuzunu kullanmak isteyebilirsiniz:
- Ayırmak/başlatmak pahalıdır.
- Sınırlı bir kaynağı temsil eder.
- Tahmin edilebilir ve sık kullanılan.
Örneğin, ASP.NET Core çerçevesi örnekleri yeniden kullanmak StringBuilder için bazı yerlerde nesne havuzunu kullanır. StringBuilder
karakter verilerini tutmak için kendi arabelleklerini ayırır ve yönetir. ASP.NET Core, özellikleri uygulamak için düzenli olarak kullanır StringBuilder
ve bunları yeniden kullanmak bir performans avantajı sağlar.
Nesne havuzu her zaman performansı iyileştirmez:
- Bir nesnenin başlatma maliyeti yüksek olmadığı sürece, nesneyi havuzdan almak genellikle daha yavaştır.
- Havuz tarafından yönetilen nesneler, havuz kaldırılana kadar ayrılmaz.
Nesne havuzu oluşturmayı yalnızca uygulamanız veya kitaplığınız için gerçekçi senaryoları kullanarak performans verilerini topladıktan sonra kullanın.
UYARI: uygulamaz ObjectPool
IDisposable
. Elden çıkarılması gereken türlerde kullanılmasını önermeyiz.ObjectPool
ASP.NET Core 3.0 ve üzeri sürümlerinde desteklenirIDisposable
.
NOT: ObjectPool ayıracağı nesne sayısına bir sınır yerleştirmez, saklayacağı nesne sayısına bir sınır yerleştirir.
Kavramlar
ObjectPool<T> - temel nesne havuzu soyutlaması. Nesneleri almak ve döndürmek için kullanılır.
PooledObjectPolicy<T>- Bir nesnenin nasıl oluşturulduğunu ve havuza geri döndürülürken nasıl sıfırlanıp sıfırlanamını özelleştirmek için bunu uygulayın. Bu, doğrudan oluşturduğunuz bir nesne havuzuna geçirilebilir.... VEYA
Create nesne havuzları oluşturmak için bir fabrika işlevi görür.
ObjectPool bir uygulamada birden çok şekilde kullanılabilir:
- Havuz örneği oluşturma.
- Bağımlılık ekleme (DI) içinde bir havuzu örnek olarak kaydetme.
- 'yi
ObjectPoolProvider<>
DI'ye kaydetme ve fabrika olarak kullanma.
ObjectPool'u kullanma
Bir nesneyi almak ve Return nesneyi döndürmek için çağrısı Get yapın. Her nesneyi döndürmeniz gerekmez. Bir nesne döndürmezseniz, bu nesne çöp olarak toplanır.
ObjectPool örneği
Aşağıdaki kod:
- Bağımlılık ekleme (DI) kapsayıcısına ekler
ObjectPoolProvider
. - DI kapsayıcısına ekler ve yapılandırır
ObjectPool<StringBuilder>
. - öğesini
BirthdayMiddleware
ekler.
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);
});
services.AddWebEncoders();
}
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>();
}
}
Aşağıdaki kod şunu uygular: 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(". ");
var encoder = context.RequestServices.GetRequiredService<HtmlEncoder>();
if (now.Day == dayOfMonth && now.Month == monthOfYear)
{
stringBuilder.Append("Happy birthday!!!");
var html = encoder.Encode(stringBuilder.ToString());
await context.Response.WriteAsync(html);
}
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!");
var html = encoder.Encode(stringBuilder.ToString());
await context.Response.WriteAsync(html);
}
}
finally // Ensure this runs even if the main code throws.
{
// Return the StringBuilder to the pool.
builderPool.Return(stringBuilder);
}
return;
}
await _next(context);
}
}
ASP.NET Core