C# 언어를 활용하여 스레드를 통해 코드를 동시에 실행하고자 하는데, DB Connection과 같은 유한한 자원을 미리 생성해 놓고 활용하고자 합니다. 이때 사용할 리소스 풀로써 만든 클래스입니다.
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 클래스로 어떤 객체 타입이든 리소스로써 담아두고 활용할 수 있습니다. 예를 들어, 아래와 같은 클래스로 말입니다.
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);
실제 리소스 풀은 스레드를 통해 활용될 때에 그 의미가 있습니다. 예를 들어 앞서 작성한 코드들을 전제로 아래의 코드처럼 말입니다.
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개의 스레드만을 동시에 실행하도록 하며, 람다 함수를 통해 실행할 스레드 코드를 지정하고 있습니다.