C#의 Resource Pool (리소스 풀)

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개의 스레드만을 동시에 실행하도록 하며, 람다 함수를 통해 실행할 스레드 코드를 지정하고 있습니다.

C#에서 PostgreSQL 사용하기

C# 언어에서 PostgreSQL 데이터베이스를 사용하기 위한 내용을 정리해 봅니다.

먼저 Visual Studio를 실행하고 C# 프로젝트를 생성합니다. 그리고 Package Manager를 실행하는데, 아래와 같은 메뉴를 통해 접근이 가능합니다.

Package Manager는 콘솔창과 비슷한 방식으로 명령을 실행할 수 있는데, .NET을 위한 PostgreSQL 라이브러리를 설치하기 위해 아래의 명령을 입력합니다.

Install-Package Npgsql -Version 4.0.4

현재 시점에서는 4.0.4가 최신버전이지만 이를 확인하기 위해 아래의 URL로 접속하기 바랍니다.

https://www.nuget.org/packages/Npgsql/

라이브러리의 설치가 성공적으로 마무리 되면 아래처럼 코드를 입력하여 PostgreSQL에 대한 조회가 가능합니다.

using (var conn = new NpgsqlConnection("host=localhost;username=postgres;password=__________;database=postgres"))
{
    try
    {
        conn.Open();
        using (var cmd = new NpgsqlCommand())
        {
            cmd.Connection = conn;

            cmd.CommandText = 
                "SELECT table_name " +
                "FROM information_schema.tables " + "" +
                "WHERE table_schema = 'public' AND table_type = 'BASE TABLE'";


            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    listBox1.Items.Add(reader.GetString(0));
                    //or listBox1.Items.Add(reader["table_name"].ToString());
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

Using 절에 추가해야 할 것은 다음과 같습니다.

using Npgsql;

끝으로 Npgsql은 하나의 Connection에 대해서 하나의 Command만을 실행할 수 있습니다. 확인한 날짜는 2019년 10월 2일자이며, 이 시점에서 최신 버전으로 테스트해 보았습니다.

[C#] 중복된 Key를 허용하며, Key로 정렬 된 해쉬맵(Dictionary)

C#의 자료구조 중 Key-Value 컨테이너는 Dictionary입니다. 경우에 따라 Key로 정렬된 Dictionary는 SortedDictionary 클래스로써 C#에서 기본적으로 제공하고 있습니다. 그런데, Dictionary이므로 Key가 중복될 수 없습니다. 중복된 값을 정렬해서 저장하기 위해서는 Set 자료 구조를 이용해야 합니다. 그런데, Set는 Key만을 저장할 수 있고 Value는 저장할 수 없습니다. 중복된 Key를 허용하면서, Key로 정렬된 컨테이너는 기본적으로 C#에서 제공하지 않습니다. 하지만 두개의 클래스를 조합하면 가능한데, SortedSet과 Tuple입니다. 이 두개의 클래스를 통해 중복된 Key를 허용하면서, Key로 정렬된 컨테이너에 대한 클래스는 아래와 같습니다.

public class SortedTupleBag<TKey, TValue> : SortedSet<Tuple<TKey, TValue>>
where TKey : IComparable
{
    private class TupleComparer : Comparer<Tuple<TKey, TValue>>
    {
        public override int Compare(Tuple<TKey, TValue> x, Tuple<TKey, TValue> y)
        {
            if (x == null || y == null) return 0;

            return x.Item1.Equals(y.Item1) ? 1 : Comparer<TKey>.Default.Compare(x.Item1, y.Item1);
        }
    }

    public SortedTupleBag() : base(new TupleComparer()) { }

    public void Add(TKey key, TValue value)
    {
        Add(new Tuple<TKey, TValue>(key, value));
    }
}

이 클래스의 활용 예는 아래의 코드와 같습니다.

var tuples = new SortedTupleBag<double, double>();

tuples.Add(10.0, 100.1);
tuples.Add(40.0, 100.4);
tuples.Add(10.0, 1000.1);
tuples.Add(80.0, 20.8);
tuples.Add(70.0, 2100.7);
tuples.Add(50.0, 200.5);

Add 함수의 첫번째는 Key이고 두번째는 Value입니다. Key의 순서대로 정렬되어 있는지 확인하기 위해 아래의 코드를 통해 살펴볼 수 있습니다.

Console.WriteLine("Sorted List");
foreach (var tuple in tuples)
{
    Console.WriteLine("{0} {1}", tuple.Item1, tuple.Item2);
}

Console.WriteLine("");

Console.WriteLine("Min: {0}, Max: {1}", tuples.Min, tuples.Max);

위 코드의 실행 결과는 아래와 같습니다.

Sorted List
10 100.1
10 1000.1
40 100.4
50 200.5
70 2100.7
80 20.8

Min: (10, 100.1), Max: (80, 20.8)

Visual Studio Code의 Python에서 코드 자동 완성 기능이 느리게 작동할 때

필자는 파이션이나 Go와 같은 언어를 위한 IDE 툴로써 Visual Studio Code를 사용합니다. 파이썬에서 코드 자동 완성 기능이 느리게 작동할때, 위의 이미지에서 보이는 Jedi Enable를 비활성화하여 IntelliSense 엔진을 Jedi가 아닌 Microsoft Python Analysis Engine을 사용하도록 지정하면 해결됩니다. 이는 기본설정이 아니므로 무엇가 부작용이 있을 수도 있다는 점을 참고하기 바랍니다.