C# 언어를 활용하여 스레드를 통해 코드를 동시에 실행하고자 하는데, DB Connection과 같은 유한한 자원을 미리 생성해 놓고 활용하고자 합니다. 이때 사용할 리소스 풀로써 만든 클래스입니다.
private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>();
private ManualResetEvent _event = new ManualResetEvent(true);
public ResourcePool(List<T> items) {
var iter = items.GetEnumerator();
_items.Add(iter.Current);
public void Release(T item)
} while (!_items.TryTake(out item));
class ResourcePool<T>
{
private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>();
private ManualResetEvent _event = new ManualResetEvent(true);
public ResourcePool(List<T> items) {
var iter = items.GetEnumerator();
while(iter.MoveNext())
{
_items.Add(iter.Current);
}
}
public void Release(T item)
{
_items.Add(item);
_event.Set();
}
public T Get()
{
T item;
do
{
if (_items.IsEmpty)
{
_event.Reset();
_event.WaitOne();
}
} while (!_items.TryTake(out item));
return item;
}
}
class ResourcePool<T>
{
private readonly ConcurrentBag<T> _items = new ConcurrentBag<T>();
private ManualResetEvent _event = new ManualResetEvent(true);
public ResourcePool(List<T> items) {
var iter = items.GetEnumerator();
while(iter.MoveNext())
{
_items.Add(iter.Current);
}
}
public void Release(T item)
{
_items.Add(item);
_event.Set();
}
public T Get()
{
T item;
do
{
if (_items.IsEmpty)
{
_event.Reset();
_event.WaitOne();
}
} while (!_items.TryTake(out item));
return item;
}
}
위의 ResourcePool은 generic 클래스로 어떤 객체 타입이든 리소스로써 담아두고 활용할 수 있습니다. 예를 들어, 아래와 같은 클래스로 말입니다.
private int _useCount = 0;
public void increaseUsingCount()
return "V: " + v + " Count: " + _useCount;
class Resource
{
public int v {
get;
set;
}
private int _useCount = 0;
public Resource(int v)
{
this.v = v;
}
public void increaseUsingCount()
{
_useCount++;
}
public string toString()
{
return "V: " + v + " Count: " + _useCount;
}
}
class Resource
{
public int v {
get;
set;
}
private int _useCount = 0;
public Resource(int v)
{
this.v = v;
}
public void increaseUsingCount()
{
_useCount++;
}
public string toString()
{
return "V: " + v + " Count: " + _useCount;
}
}
이 리소스 풀에 스레드에서 사용할 리소스를 담아두는 코드의 예는 아래와 같습니다.
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) };
ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) };
ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);
List<Resource> items = new List<Resource>() { new Resource(0), new Resource(1), new Resource(2), new Resource(3) };
ResourcePool<Resource> objPool = new ResourcePool<Resource>(items);
실제 리소스 풀은 스레드를 통해 활용될 때에 그 의미가 있습니다. 예를 들어 앞서 작성한 코드들을 전제로 아래의 코드처럼 말입니다.
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 },
Resource r = objPool.Get();
Console.WriteLine("[Thread-Index: " + i + "] " + r.toString());
Thread.Sleep(new Random().Next(600, 3000));
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 },
(int i) => {
Resource r = objPool.Get();
r.increaseUsingCount();
Console.WriteLine("[Thread-Index: " + i + "] " + r.toString());
// Some Operation
Thread.Sleep(new Random().Next(600, 3000));
// .
objPool.Release(r);
});
Parallel.For(1, 50, new ParallelOptions { MaxDegreeOfParallelism = 8 },
(int i) => {
Resource r = objPool.Get();
r.increaseUsingCount();
Console.WriteLine("[Thread-Index: " + i + "] " + r.toString());
// Some Operation
Thread.Sleep(new Random().Next(600, 3000));
// .
objPool.Release(r);
});
위의 코드는 총 49개의 스레드를 만들고, MaxDegreeOfParallelism에서 지정한 값이 8만큼, 최대 8개의 스레드만을 동시에 실행하도록 하며, 람다 함수를 통해 실행할 스레드 코드를 지정하고 있습니다.