ASP.NET Core에서 ObjectPool로 개체 다시 사용
참고
이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
중요
이 정보는 상업적으로 출시되기 전에 실질적으로 수정될 수 있는 시험판 제품과 관련이 있습니다. Microsoft는 여기에 제공된 정보에 대해 어떠한 명시적, 또는 묵시적인 보증을 하지 않습니다.
현재 릴리스는 이 문서의 .NET 9 버전을 참조 하세요.
Microsoft.Extensions.ObjectPool은 개체들이 가비지 수집되지 않고 메모리에 계속 남아 재사용될 수 있도록 지원하는 ASP.NET Core 인프라의 일부입니다.
Microsoft.Extensions.ObjectPool
의 모든 정적 및 인스턴스 메서드는 스레드로부터 안전합니다.
관리형 개체가 다음과 같은 경우 앱에서 개체 풀을 사용할 수 있습니다.
- 할당/초기화 비용이 많이 듭니다.
- 제한된 리소스를 나타냅니다.
- 예측 가능하고 자주 사용됩니다.
예를 들어 ASP.NET Core 프레임워크는 일부 위치에 있는 개체 풀을 사용하여 StringBuilder 인스턴스를 다시 사용합니다.
StringBuilder
는 문자 데이터를 보관할 자체 버퍼를 할당하고 관리합니다. ASP.NET Core는 정기적으로 StringBuilder
를 사용하여 기능을 구현하고, 재사용으로 성능 이점을 제공합니다.
개체 풀링이 항상 성능을 향상시키는 것은 아닙니다.
- 개체의 초기화 비용이 높지 않으면 일반적으로 풀에서 개체를 얻는 것이 더 느립니다.
- 풀에서 관리하는 개체는 풀이 할당 해제될 때까지 할당이 해제되지 않습니다.
앱 또는 라이브러리에 대한 현실적인 시나리오를 사용하여 성능 데이터를 수집한 후에만 개체 풀링을 사용합니다.
참고: ObjectPool은 할당 개체 수에 제한을 두지 않고 보존 개체 수에 제한을 둡니다.
ObjectPool 개념
DefaultObjectPoolProvider가 사용되고 T
가 IDisposable
을 구현하는 경우:
- 풀에 반환되지 않는 항목은 삭제됩니다.
- DI에서 풀이 삭제되면 풀의 모든 항목이 삭제됩니다.
참고: 풀이 폐기된 후:
-
Get
을 호출하면ObjectDisposedException
이 throw됩니다. -
Return
을 호출하면 지정된 항목이 삭제됩니다.
중요한 ObjectPool
형식 및 인터페이스:
- ObjectPool<T> : 기본 개체 풀 추상화입니다. 이 메서드는 개체를 얻고 반환하는 데 사용됩니다.
- PooledObjectPolicy<T> : 개체를 만드는 방법과 풀로 반환할 때 개체를 다시 설정하는 방법을 사용자 지정하도록 구현합니다. 직접 생성된 객체 풀에 이를 전달할 수 있습니다.
- IResettable : 개체 풀로 반환되면 개체를 자동으로 다시 설정합니다.
ObjectPool은 다음과 같은 여러 가지 방법으로 앱에서 사용할 수 있습니다.
- 풀을 생성합니다.
- DI(종속성 주입)에서 풀을 인스턴스로 등록합니다.
- DI에
ObjectPoolProvider<>
를 등록하고 팩터리로 사용합니다.
ObjectPool을 사용하는 방법
Get을 호출하여 개체를 얻고 Return을 호출하여 개체를 반환합니다. 모든 개체를 반환할 필요가 없습니다. 개체가 반환되지 않으면 가비지 수집됩니다.
ObjectPool 예제
코드는 다음과 같습니다.
-
ObjectPoolProvider
를 DI(종속성 주입) 컨테이너에 추가합니다. - 개체 풀로
IResettable
반환되는 경우 버퍼의 내용을 자동으로 지우는 인터페이스를 구현합니다.
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;
}
}
참고: 풀된 형식 T
이 구현 IResettable
되지 않는 경우 풀로 반환되기 전에 사용자 지정 PooledObjectPolicy<T>
을 사용하여 개체의 상태를 다시 설정할 수 있습니다.
Microsoft.Extensions.ObjectPool은 객체를 가비지 수집에서 벗어나 다시 사용할 수 있도록 메모리에 객체 그룹을 유지하는 데 도움을 주는 ASP.NET Core 인프라의 일부입니다.
Microsoft.Extensions.ObjectPool
의 모든 정적 및 인스턴스 메서드는 스레드로부터 안전합니다.
관리형 개체가 다음과 같은 경우 앱에서 개체 풀을 사용할 수 있습니다.
- 할당/초기화 비용이 많이 듭니다.
- 제한된 리소스를 나타냅니다.
- 예측 가능하고 자주 사용됩니다.
예를 들어 ASP.NET Core 프레임워크는 일부 위치에 있는 개체 풀을 사용하여 StringBuilder 인스턴스를 다시 사용합니다.
StringBuilder
는 문자 데이터를 보관할 자체 버퍼를 할당하고 관리합니다. ASP.NET Core는 정기적으로 StringBuilder
를 사용하여 기능을 구현하고, 재사용으로 성능 이점을 제공합니다.
개체 풀링이 항상 성능을 향상시키는 것은 아닙니다.
- 개체의 초기화 비용이 높지 않으면 일반적으로 풀에서 개체를 얻는 것이 더 느립니다.
- 풀에서 관리하는 개체는 풀이 할당 해제될 때까지 할당이 해제되지 않습니다.
앱 또는 라이브러리에 대한 현실적인 시나리오를 사용하여 성능 데이터를 수집한 후에만 개체 풀링을 사용합니다.
참고: ObjectPool은 할당 개체 수에 제한을 두지 않고 보존 개체 수에 제한을 둡니다.
개념
DefaultObjectPoolProvider가 사용되고 T
가 IDisposable
을 구현하는 경우:
- 풀에 반환되지 않는 항목은 삭제됩니다.
- DI에서 풀이 삭제되면 풀의 모든 항목이 삭제됩니다.
참고: 풀이 처리된 후:
-
Get
을 호출하면ObjectDisposedException
이 throw됩니다. -
Return
을 호출하면 지정된 항목이 삭제됩니다.
중요한 ObjectPool
형식 및 인터페이스:
- ObjectPool<T> : 기본 개체 풀 추상화입니다. 개체를 가져오고 반환하는 데 사용됩니다.
- PooledObjectPolicy<T> : 개체를 만드는 방법과 풀로 반환할 때 개체를 다시 설정하는 방법을 사용자 지정하도록 구현합니다. 직접 구성되는 객체 풀에 전달하거나
- Create : 객체 풀을 생성하는 공장의 역할을 합니다.
- IResettable: 개체 풀로 반환되면 개체를 자동으로 다시 설정합니다.
ObjectPool은 다음과 같은 여러 가지 방법으로 앱에서 사용할 수 있습니다.
- 풀을 인스턴스화합니다.
- DI(종속성 주입)에서 풀을 인스턴스로 등록합니다.
- DI에
ObjectPoolProvider<>
를 등록하고 팩터리로 사용합니다.
ObjectPool을 사용하는 방법
Get을 호출하여 개체를 얻고 Return을 호출하여 개체를 반환합니다. 모든 개체를 반환할 필요는 없습니다. 개체를 반환하지 않으면 가비지 수집이 수행됩니다.
ObjectPool 샘플
코드는 다음과 같습니다.
-
ObjectPoolProvider
를 DI(종속성 주입) 컨테이너에 추가합니다. -
ObjectPool<StringBuilder>
를 DI 컨테이너에 추가하고 구성합니다. -
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();
다음 코드는 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은 개체의 가비지 수집을 허용하는 대신 다시 사용할 수 있도록 메모리에 개체 그룹을 유지하도록 지원하는 ASP.NET Core 인프라의 일부입니다.
관리되는 개체가 다음과 같은 경우 개체 풀을 사용할 수 있습니다.
- 할당/초기화 비용이 많이 듭니다.
- 제한된 리소스를 나타냅니다.
- 예측 가능하고 자주 사용됩니다.
예를 들어 ASP.NET Core 프레임워크는 일부 위치에 있는 개체 풀을 사용하여 StringBuilder 인스턴스를 다시 사용합니다.
StringBuilder
는 문자 데이터를 보관할 자체 버퍼를 할당하고 관리합니다. ASP.NET Core는 정기적으로 StringBuilder
를 사용하여 기능을 구현하고, 재사용으로 성능 이점을 제공합니다.
개체 풀링이 항상 성능을 향상시키는 것은 아닙니다.
- 개체의 초기화 비용이 높지 않으면 일반적으로 풀에서 개체를 얻는 것이 더 느립니다.
- 풀에서 관리하는 개체는 풀이 할당 해제될 때까지 할당이 해제되지 않습니다.
앱 또는 라이브러리에 대한 현실적인 시나리오를 사용하여 성능 데이터를 수집한 후에만 개체 풀링을 사용합니다.
경고: ObjectPool
은 IDisposable
을 구현하지 않습니다. 삭제가 필요한 형식에는 사용하지 않는 것이 좋습니다.
ObjectPool
ASP.NET Core 3.0 이상에서 지원합니다IDisposable
.
참고: ObjectPool은 할당할 개체 수에 제한을 두지 않고 보존할 개체 수에 제한을 둡니다.
개념
ObjectPool<T> - 기본 개체 풀 추상화입니다. 개체를 얻고 반환하는 데 사용됩니다.
PooledObjectPolicy<T> - 개체를 만드는 방법 및 풀에 반환할 때 개체를 초기화하는 방법을 사용자 지정하기 위해 이를 구현합니다. 직접 생성한 개체 풀에 전달할 수 있습니다. 또는
Create는 개체 풀을 만들기 위한 팩터리 역할을 합니다.
ObjectPool은 다음과 같은 여러 가지 방법으로 앱에서 사용할 수 있습니다.
- 풀을 인스턴스화합니다.
- DI(종속성 주입)에서 풀을 인스턴스로 등록합니다.
- DI에
ObjectPoolProvider<>
를 등록하고 팩터리로 사용합니다.
ObjectPool을 사용하는 방법
Get을 호출하여 개체를 얻고 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);
});
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>();
}
}
다음 코드는 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