.NET Coreでオブジェクトプールを使用する
<スパン I. オブジェクトプールとは
<ブロッククオートオブジェクトプールとは、簡単に言えば、オブジェクトに再利用性を持たせるためのソフトウェア設計の考え方である。よく「オブジェクトを借りて返すのは簡単だ」と言いますが、オブジェクトプーリングは、オブジェクトを借りて返すという2つの動作で再利用できるようにすることで、オブジェクトを頻繁に生成することによるパフォーマンスのオーバーヘッドを節約する方法なのです。オブジェクトプーリングの最も一般的なシナリオはゲームデザインです。ゲームでは、再利用可能なオブジェクトが多数存在し、一定量の弾丸が出現し、周期的に再生成されることはないからです。データベースにはコネクションプールと呼ばれるものがあり、データベースへの接続に失敗すると、経験豊富な開発者はまずコネクションプールが満杯かどうかを確認する傾向があります。これは実は、特定のドメインにおけるオブジェクトプーリングパターンの具体的な実装なのです。つまり、オブジェクトプールとは本質的に、一連のオブジェクトの生成と破棄を担当するコンテナなのだ。オブジェクト・プールの最大の利点は、プール内の各オブジェクトを自律的に管理し、リサイクルする必要があるか、再利用可能かどうかを判断できることです。新しいオブジェクトを作成するとシステム・リソースを消費することは周知の事実ですが、これらのオブジェクトを再利用できれば、システム・リソースのオーバーヘッドを節約でき、システムのパフォーマンス向上に非常に役立ちます。次のコードは、Microsoftの公式ドキュメントにあるシンプルなオブジェクトプールを実装したものです。
public class ObjectPool<T> : IObjectPool<T>
{
private Func<T> _instanceFactory;
private ConcurrentBag<T> _instanceItems;
public ObjectPool(Func<T> instanceFactory)
{
_instanceFactory = instanceFactory ?
throw new ArgumentNullException(nameof(instanceFactory));
_instanceItems = new ConcurrentBag<T>();
}
public T Get()
{
T item;
if (_instanceItems.TryTake(out item)) return item;
return _instanceFactory();
}
public void Return(T item)
{
_instanceItems.Add(item);
}
}
NET Coreのオブジェクトプーリング
での
NET Core
マイクロソフトは、オブジェクトプーリングの実装を
Microsoft.Extensions.ObjectPool
. これは、次の3つの主要なコアコンポーネントを提供します。
ObjectPool
ObjectPoolProvider
and
IPooledObjectPolicy
.
ObjectPool
is an abstract class that provides external methods Get and Return, which are called borrow and return.
ObjectPoolProvider
is also an abstract class whose responsibility is to create ObjectPool, it provides two Create methods, the difference between the two is that the parameterless version essentially uses the
DefaultPooledObjectPolicy
. It is the same as the
DefaultObjectPool
DefaultObjectPool uses an ObjectWrapper[] to manage objects internally. The size of the ObjectWrapper[] is equal to maximumRetained-1, and by default maximumRetained is equal to
Environment.ProcessorCount * 2
The main use of
Interlocked.CompareExchange()
method.
The specific code is as follows.
public override T Get()
{
var item = _firstItem;
CompareExchange(ref _firstItem, null, item) if (item == null || Interlocked. = item)
{
var items = _items;
for (var i = 0; i < items.Length; i++)
{
item = items[i].Element;
if (item ! = null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)
{
return item;
}
}
item = Create();
}
return item;
}
// Non-inline to improve its code quality as uncommon path
[MethodImpl(MethodImplOptions.NoInlining)]
private T Create() => _fastPolicy? Create(); _policy;
public override void Return(T obj)
{
Return(obj) if (_isDefaultPolicy || (_fastPolicy?. _policy.Return(obj)))
{
if (_firstItem ! = CompareExchange(ref _firstItem, obj, null) ! = null)
{
var items = _items;
for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) ! = null; ++i)
{
}
}
}
}
Here we use
Interlocked.CompareExchange()
method, the Get() method will
items[i].Element
and
null
The Return() method swaps the specified element to null and returns the original value.
items[i].Element
and obj are swapped to a value other than null, indicating that the specified element has been returned. This method will only swap if the first and third arguments are equal.
Having said that, let's look at the specific usage of the object pool.
var service = new ServiceCollection();
//use DefaultObjectPoolProvider
service.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
// Use the default policy
service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>
{
var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
return objectPoolProvider.Create<Foo>();
});
//Use a custom policy
service.AddSingleton<ObjectPool<Foo>>(serviceProvider =>
{
var objectPoolProvider = serviceProvider.GetRequiredService<ObjectPoolProvider>();
return objectPoolProvider.Create(new FooObjectPoolPolicy());
});
var serviceProvider = _service.BuildServiceProvider();
var objectPool = _serviceProvider.GetService<ObjectPool<Foo>>();
// there is a borrow and a return, twice is the same object
var item1 = objectPool.Get();
Get(); objectPool.Return(item1);
var item2 = objectPool.Get();
Assert.AreEqual(item1, item2);/
// there is a borrowing but not a return, two different objects
var item3 = objectPool.Get();
var item4 = objectPool.Get();
Assert.AreEqual(item3, item4);/
In the above code Foo and FooObjectPoolPolicy are two tool classes.
public class Foo
{
public string Id { get; set; }
public DateTime? CreatedAt { get; set; }
public string CreatedBy { get; set; }
}
public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>
{
public Foo Create()
{
return new Foo()
{
Id = Guid.NewGuid().ToString("N"),
CreatedAt = DateTime.Now,
CreatedBy = "zs"
};
}
public bool Return(Foo obj)
{
return true;
}
}
TIP.
When you need to control how objects within the object pool are created, you might consider implementing a custom
IPooledObjectPolicy
and vice versa
DefaultPooledObjectPolicy
implementation is perfectly fine for your use.
III. Summary of this article
Implementing an object pool can be considered
ConcurrentBag
ConcurrentBag, Stack.
Queue
and
BlockingCollection
NET Core, Microsoft has implemented a simple object pool for us, and in most cases, we only need to define our own
{NET Core.
IPooledObjectPolicy
GameObject
public class Foo
{
public string Id { get; set; }
public DateTime? CreatedAt { get; set; }
public string CreatedBy { get; set; }
}
public class FooObjectPoolPolicy : IPooledObjectPolicy<Foo>
{
public Foo Create()
{
return new Foo()
{
Id = Guid.NewGuid().ToString("N"),
CreatedAt = DateTime.Now,
CreatedBy = "zs"
};
}
public bool Return(Foo obj)
{
return true;
}
}
DefaultPooledObjectPolicy
で、オブジェクトの貸し借りを決定します。要するに、ゲームの世界である
GameObject
とデータベースのコネクションプールは、それぞれのドメインにおけるオブジェクトプーリングパターンの具体的な実装である。
TIP オブジェクトプーリングは、オブジェクトを再利用することでリソースのオーバーヘッドを減らし、その結果システムのパフォーマンスを向上させるソフトウェアのデザインパターンである。その核心は、システムによる積極的なリサイクルを回避するためにコンテナ内のオブジェクトのライフサイクルを制御することであり、オブジェクトプールから借りたオブジェクトは適時に返却しなければならず、さもなければオブジェクトプールに利用可能なリソースが存在しないことになる。
NET Core Object Poolのサポートは、過去の記事を検索するか、以下の関連記事を引き続き閲覧してください!
関連
-
.NET 6:.NETのロギングコンポーネントlog4netを使用する。
-
NET6新機能 - 暗黙の名前空間参照
-
.NET複数データベース一括データ挿入、更新(SqlServer、MySql、PgSql、Oracleをサポートします。)
-
ASP.NET Core Dependency Injectionフレームワークの活用
-
ASP.NET Core ディペンデンシーインジェクションの詳細
-
ASP.NET CoreでCAPの取引詳細を自動で有効にする
-
Net Core HttpClient処理 レスポンス圧縮の詳細
-
ASP.NET Core Web API チュートリアル プロジェクト構成図
-
再起動を伴わないNET5の設定変更は自動的に反映される
-
ネットパフォーマンスチューニング - ArrayPool 詳細
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
NET6新機能 新構造体の最適化
-
30分でわかるコング経由の.NETゲートウェイ
-
ASP.NET Coreミドルウェアによるグローバル例外処理機構の利用について
-
ASP.NET学習でよくあるエラーのまとめ
-
ASP.NETでWeb.configからログインする際の正しいアカウントパスワードを確認する
-
ASP.NET Core MVC Dependency Injection ビューとコントローラ
-
.NET 6における暗黙の名前空間参照
-
asp.net core3.1 cookieとjwtのハイブリッド認証による多様な認証ソリューションの実現
-
Application_End イベントをブロックする解決策
-
CS0234 名前空間 'Microsoft.AspNet' に型または名前空間名 'Mvc' が存在しない (あなたは