Ponowne użycie obiektu z pulą obiektów w ASP.NET Core
Przez Günther Foidl, Steve Gordon i Samson Amaugo
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Microsoft.Extensions.ObjectPool jest częścią infrastruktury ASP.NET Core, która obsługuje przechowywanie grupy obiektów w pamięci do ponownego użycia, zamiast zezwalać na wyrzucanie elementów bezużytecznych. Wszystkie metody statyczne i metody wystąpień w programie Microsoft.Extensions.ObjectPool
są bezpieczne wątkowo.
Aplikacje mogą chcieć użyć puli obiektów, jeśli obiekty zarządzane są:
- Kosztowna alokacja/inicjowanie.
- Reprezentuje ograniczony zasób.
- Używano przewidywalnie i często.
Na przykład platforma ASP.NET Core używa puli obiektów w niektórych miejscach do ponownego użycia StringBuilder wystąpień. StringBuilder
przydziela własne i zarządza nimi w celu przechowywania danych znaków. ASP.NET Core regularnie używa StringBuilder
do implementowania funkcji i ponownego korzystania z nich zapewnia korzyść z wydajności.
Buforowanie obiektów nie zawsze poprawia wydajność:
- O ile koszt inicjowania obiektu nie jest wysoki, zwykle jest wolniejsze, aby uzyskać obiekt z puli.
- Obiekty zarządzane przez pulę nie zostaną przydzielone do momentu anulowania przydziału puli.
Używaj buforowania obiektów tylko po zebraniu danych wydajności przy użyciu realistycznych scenariuszy dla aplikacji lub biblioteki.
UWAGA: Pula obiektów nie nakłada limitu liczby przydzielanych obiektów, nakłada limit liczby zachowanych obiektów.
Pojęcia dotyczące puli obiektów
Kiedy DefaultObjectPoolProvider jest używany i T
implementuje IDisposable
:
- Elementy, które nie zostaną zwrócone do puli, zostaną usunięte.
- Gdy pula zostanie usunięta przez di, wszystkie elementy w puli zostaną usunięte.
UWAGA: Po usunięciu puli:
- Wywołanie wywołania
Get
zgłasza błądObjectDisposedException
. - Wywołanie metody
Return
usuwa dany element.
Ważne ObjectPool
typy i interfejsy:
- ObjectPool<T> : Abstrakcja podstawowej puli obiektów. Służy do pobierania i zwracania obiektów.
- PooledObjectPolicy<T> : Zaimplementuj to, aby dostosować sposób tworzenia obiektu i sposób jego resetowania po powrocie do puli. Można to przekazać do puli obiektów, która jest skonstruowana bezpośrednio.
- IResettable : Automatycznie resetuje obiekt po powrocie do puli obiektów.
Pula obiektów może być używana w aplikacji na wiele sposobów:
- Utworzenie wystąpienia puli.
- Rejestrowanie puli w iniekcji zależności (DI) jako wystąpienia.
- Rejestrowanie elementu
ObjectPoolProvider<>
w usłudze DI i używanie go jako fabryki.
Jak używać puli obiektów
Wywołaj metodę Get , aby uzyskać obiekt i Return zwrócić obiekt. Nie ma potrzeby zwracania każdego obiektu. Jeśli obiekt nie zostanie zwrócony, zostanie on odśmiecany.
Przykład puli obiektów
Następujący kod powoduje:
- Dodaje
ObjectPoolProvider
do kontenera wstrzykiwania zależności (DI). - Implementuje interfejs,
IResettable
aby automatycznie wyczyścić zawartość buforu po powrocie do puli obiektów.
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;
}
}
UWAGA: Gdy typ T
w puli nie implementuje IResettable
elementu , można użyć niestandardowego PooledObjectPolicy<T>
do zresetowania stanu obiektów przed ich zwróceniem do puli.
Microsoft.Extensions.ObjectPool jest częścią infrastruktury ASP.NET Core, która obsługuje przechowywanie grupy obiektów w pamięci do ponownego użycia, zamiast zezwalać na wyrzucanie elementów bezużytecznych. Wszystkie metody statyczne i metody wystąpień w programie Microsoft.Extensions.ObjectPool
są bezpieczne wątkowo.
Aplikacje mogą chcieć użyć puli obiektów, jeśli obiekty zarządzane są:
- Kosztowna alokacja/inicjowanie.
- Reprezentuje ograniczony zasób.
- Używano przewidywalnie i często.
Na przykład platforma ASP.NET Core używa puli obiektów w niektórych miejscach do ponownego użycia StringBuilder wystąpień. StringBuilder
przydziela własne i zarządza nimi w celu przechowywania danych znaków. ASP.NET Core regularnie używa StringBuilder
do implementowania funkcji i ponownego korzystania z nich zapewnia korzyść z wydajności.
Buforowanie obiektów nie zawsze poprawia wydajność:
- O ile koszt inicjowania obiektu nie jest wysoki, zwykle jest wolniejsze, aby uzyskać obiekt z puli.
- Obiekty zarządzane przez pulę nie zostaną przydzielone do momentu anulowania przydziału puli.
Używaj buforowania obiektów tylko po zebraniu danych wydajności przy użyciu realistycznych scenariuszy dla aplikacji lub biblioteki.
UWAGA: Pula obiektów nie nakłada limitu liczby przydzielanych obiektów, nakłada limit liczby zachowanych obiektów.
Pojęcia
Kiedy DefaultObjectPoolProvider jest używany i T
implementuje IDisposable
:
- Elementy, które nie zostaną zwrócone do puli, zostaną usunięte.
- Gdy pula zostanie usunięta przez di, wszystkie elementy w puli zostaną usunięte.
UWAGA: Po usunięciu puli:
- Wywołanie wywołania
Get
zgłasza błądObjectDisposedException
. - Wywołanie metody
Return
usuwa dany element.
Ważne ObjectPool
typy i interfejsy:
- ObjectPool<T> : Abstrakcja podstawowej puli obiektów. Służy do pobierania i zwracania obiektów.
- PooledObjectPolicy<T> : Zaimplementuj to, aby dostosować sposób tworzenia obiektu i sposób jego resetowania po powrocie do puli. Można to przekazać do puli obiektów, która jest konstruowana bezpośrednio lub
- Create : działa jako fabryka do tworzenia pul obiektów.
- IResettable : automatycznie resetuje obiekt po powrocie do puli obiektów.
Pula obiektów może być używana w aplikacji na wiele sposobów:
- Utworzenie wystąpienia puli.
- Rejestrowanie puli w iniekcji zależności (DI) jako wystąpienia.
- Rejestrowanie elementu
ObjectPoolProvider<>
w usłudze DI i używanie go jako fabryki.
Jak używać puli obiektów
Wywołaj metodę Get , aby uzyskać obiekt i Return zwrócić obiekt. Nie ma potrzeby zwracania każdego obiektu. Jeśli nie zwrócisz obiektu, zostanie on odebrany bezużytecznie.
Przykład puli obiektów
Następujący kod powoduje:
- Dodaje
ObjectPoolProvider
do kontenera wstrzykiwania zależności (DI). - Dodaje i konfiguruje
ObjectPool<StringBuilder>
kontener DI. - Dodaje element
BirthdayMiddleware
.
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();
Poniższy kod implementuje 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 jest częścią infrastruktury ASP.NET Core, która obsługuje przechowywanie grupy obiektów w pamięci do ponownego użycia, zamiast zezwalać na wyrzucanie elementów bezużytecznych.
Jeśli zarządzane obiekty są zarządzane, warto użyć puli obiektów:
- Kosztowna alokacja/inicjowanie.
- Reprezentuje ograniczony zasób.
- Używano przewidywalnie i często.
Na przykład platforma ASP.NET Core używa puli obiektów w niektórych miejscach do ponownego użycia StringBuilder wystąpień. StringBuilder
przydziela własne i zarządza nimi w celu przechowywania danych znaków. ASP.NET Core regularnie używa StringBuilder
do implementowania funkcji i ponownego korzystania z nich zapewnia korzyść z wydajności.
Buforowanie obiektów nie zawsze poprawia wydajność:
- O ile koszt inicjowania obiektu nie jest wysoki, zwykle jest wolniejsze, aby uzyskać obiekt z puli.
- Obiekty zarządzane przez pulę nie zostaną przydzielone do momentu anulowania przydziału puli.
Używaj buforowania obiektów tylko po zebraniu danych wydajności przy użyciu realistycznych scenariuszy dla aplikacji lub biblioteki.
OSTRZEŻENIE: Element ObjectPool
nie implementuje IDisposable
elementu . Nie zalecamy używania go z typami, które wymagają usuwania.ObjectPool
w systemie ASP.NET Core 3.0 lub nowszym obsługuje program IDisposable
.
UWAGA: Pula obiektów nie nakłada limitu liczby obiektów, które przydzieli, nakłada limit liczby zachowanych obiektów.
Pojęcia
ObjectPool<T> - abstrakcja podstawowej puli obiektów. Służy do pobierania i zwracania obiektów.
PooledObjectPolicy<T> — zaimplementuj to, aby dostosować sposób tworzenia obiektu i sposób jego resetowania po powrocie do puli. Można to przekazać bezpośrednio do puli obiektów, którą skonstruujesz bezpośrednio.... LUB
Create działa jako fabryka tworzenia pul obiektów.
Pula obiektów może być używana w aplikacji na wiele sposobów:
- Utworzenie wystąpienia puli.
- Rejestrowanie puli w iniekcji zależności (DI) jako wystąpienia.
- Rejestrowanie elementu
ObjectPoolProvider<>
w usłudze DI i używanie go jako fabryki.
Jak używać puli obiektów
Wywołaj metodę Get , aby uzyskać obiekt i Return zwrócić obiekt. Nie ma potrzeby zwracania każdego obiektu. Jeśli nie zwrócisz obiektu, zostanie on odebrany bezużytecznie.
Przykład puli obiektów
Następujący kod powoduje:
- Dodaje
ObjectPoolProvider
do kontenera wstrzykiwania zależności (DI). - Dodaje i konfiguruje
ObjectPool<StringBuilder>
kontener DI. - Dodaje element
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);
});
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>();
}
}
Poniższy kod implementuje 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);
}
}