作成者 : Günther Foidl、 Steve Gordon、 Samson Amaugo
注
これは、この記事の最新バージョンではありません。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、 この記事の .NET 10 バージョンを参照してください。
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がスローされます。 -
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がスローされます。 -
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