ConcurrentBag을 사용하여 개체 풀 만들기
이 예제에서는 ConcurrentBag<T>를 사용하여 개체 풀을 구현하는 방법을 보여 줍니다. 클래스에 여러 인스턴스가 필요하고 클래스를 만들거나 삭제하는 데 비용이 많이 드는 경우 개체 풀을 사용하면 애플리케이션 성능을 향상시킬 수 있습니다. 클라이언트 프로그램에서 새 개체를 요청하면 먼저 개체 풀이 이미 풀에 만들어져 반환된 개체를 제공하려고 시도합니다. 제공될 개체가 없는 경우에만 새 개체가 만들어집니다.
ConcurrentBag<T>는 특히 한 스레드에서 항목의 추가와 제거를 모두 수행할 때 추가하고 제거하는 속도가 빠르기 때문에 개체를 저장하는 데 사용됩니다. ConcurrentQueue<T> 및 ConcurrentStack<T>와 같이 모음 데이터 구조가 구현하는 IProducerConsumerCollection<T>를 중심으로 구축되도록 이 예제를 강화할 수 있습니다.
팁
이 문서에서는 다시 사용할 개체를 저장하기 위해 기본 동시 형식으로 개체 풀의 고유한 구현을 작성하는 방법을 정의합니다. 그러나 Microsoft.Extensions.ObjectPool.ObjectPool<T> 형식은 이미 Microsoft.Extensions.ObjectPool 네임스페이스에 있습니다. 다양한 추가 기능을 포함하는 자체 구현을 만들기 전에 제공된 형식을 사용하는 것이 좋습니다.
예시
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace ObjectPoolExample
{
public class ObjectPool<T>
{
private readonly ConcurrentBag<T> _objects;
private readonly Func<T> _objectGenerator;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
_objects = new ConcurrentBag<T>();
}
public T Get() => _objects.TryTake(out T item) ? item : _objectGenerator();
public void Return(T item) => _objects.Add(item);
}
class Program
{
static void Main(string[] args)
{
using var cts = new CancellationTokenSource();
// Create an opportunity for the user to cancel.
_ = Task.Run(() =>
{
if (char.ToUpperInvariant(Console.ReadKey().KeyChar) == 'C')
{
cts.Cancel();
}
});
var pool = new ObjectPool<ExampleObject>(() => new ExampleObject());
// Create a high demand for ExampleObject instance.
Parallel.For(0, 1000000, (i, loopState) =>
{
var example = pool.Get();
try
{
Console.CursorLeft = 0;
// This is the bottleneck in our application. All threads in this loop
// must serialize their access to the static Console class.
Console.WriteLine($"{example.GetValue(i):####.####}");
}
finally
{
pool.Return(example);
}
if (cts.Token.IsCancellationRequested)
{
loopState.Stop();
}
});
Console.WriteLine("Press the Enter key to exit.");
Console.ReadLine();
}
}
// A toy class that requires some resources to create.
// You can experiment here to measure the performance of the
// object pool vs. ordinary instantiation.
class ExampleObject
{
public int[] Nums { get; set; }
public ExampleObject()
{
Nums = new int[1000000];
var rand = new Random();
for (int i = 0; i < Nums.Length; i++)
{
Nums[i] = rand.Next();
}
}
public double GetValue(long i) => Math.Sqrt(Nums[i]);
}
}
Imports System.Collections.Concurrent
Imports System.Threading
Imports System.Threading.Tasks
Module ObjectPoolExample
Public Class ObjectPool(Of T)
Private _objects As ConcurrentBag(Of T)
Private _objectGenerator As Func(Of T)
Public Sub New(ByVal objectGenerator As Func(Of T))
If objectGenerator Is Nothing Then Throw New ArgumentNullException("objectGenerator")
_objects = New ConcurrentBag(Of T)()
_objectGenerator = objectGenerator
End Sub
Public Function GetObject() As T
Dim item As T
If _objects.TryTake(item) Then Return item
Return _objectGenerator()
End Function
Public Sub PutObject(ByVal item As T)
_objects.Add(item)
End Sub
End Class
Sub Main()
Dim cts As CancellationTokenSource = New CancellationTokenSource()
' Create an opportunity for the user to cancel.
Task.Run(Sub()
If Console.ReadKey().KeyChar = "c"c Or Console.ReadKey().KeyChar = "C"c Then
cts.Cancel()
End If
End Sub)
Dim pool As ObjectPool(Of TestClass) = New ObjectPool(Of TestClass)(Function() New TestClass())
' Create a high demand for TestClass objects.
Parallel.For(0, 1000000, Sub(i, loopState)
Dim mc As TestClass = pool.GetObject()
Console.CursorLeft = 0
' This is the bottleneck in our application. All threads in this loop
' must serialize their access to the static Console class.
Console.WriteLine("{0:####.####}", mc.GetValue(i))
pool.PutObject(mc)
If cts.Token.IsCancellationRequested Then
loopState.Stop()
End If
End Sub)
Console.WriteLine("Press the Enter key to exit.")
Console.ReadLine()
cts.Dispose()
End Sub
' A toy class that requires some resources to create.
' You can experiment here to measure the performance of the
' object pool vs. ordinary instantiation.
Class TestClass
Public Nums() As Integer
Public Function GetValue(ByVal i As Long) As Double
Return Math.Sqrt(Nums(i))
End Function
Public Sub New()
Nums = New Integer(10000000) {}
' ReDim Nums(1000000)
Dim rand As Random = New Random()
For i As Integer = 0 To Nums.Length - 1
Nums(i) = rand.Next()
Next
End Sub
End Class
End Module
참고 항목
GitHub에서 Microsoft와 공동 작업
이 콘텐츠의 원본은 GitHub에서 찾을 수 있으며, 여기서 문제와 끌어오기 요청을 만들고 검토할 수도 있습니다. 자세한 내용은 참여자 가이드를 참조하세요.
.NET