본문 바로가기
Study

C#, 쓰레드 동기화를 위한 뮤텍스와 셰마포어 예제

by Client. DJ 2022. 2. 2.
반응형

쓰레드 동기화


쓰레드 동기화법 중에서는 대표적으로 아래 두 가지가 있습니다.

  • 뮤텍스(Mutex)
  • 셰마포어(Semaphore)

다중 쓰레드를 사용하게 된다면, 동기화 작업은 필수로 구성되어야합니다. 자칫 잘못 사용할 경우, 데드락에 걸릴 수 있으며, 쓰레드간 호출 순서가 보장되지 않아, 전역으로 선언된 값들이 의도치 않게 변경될 수도 있습니다. 동기화법에 관련되어서 예제와 함께 차근차근 알아보겠습니다.

 

뮤텍스(Mutex)


한 개의 자원씩 순서대로 사용하는 뮤텍스

뮤텍스는 기본적으로 하나의 자원을 순차적으로 사용하도록 요구하는 구조입니다. 쓰레드1, 쓰레드2, 쓰레드3 ... 등등 여러 쓰레드가 한 곳을 가리킨다면 순서대로 연산이 완료될 때까지 기다렸다가 순서를 넘겨주는 방식입니다.

 

아래의 코드는 여러개의 쓰레드가 순차적으로 실행이 됩니다.

using System.Threading;

class Program
{
    private static void Main(string[] args)
    {
        TestClass testClass = new TestClass();
        Thread thread1 = new Thread(testClass.Calculte);
        Thread thread2 = new Thread(testClass.Calculte);
        Thread thread3 = new Thread(testClass.Calculte);
        thread1.Start();
        thread2.Start();
        thread3.Start();
    }

    private class TestClass
    {
        Mutex mutex = new Mutex(false, "Calculte");

        public void Calculte()
        {
            // 뮤텍스 취득할 때까지 기다린다.
            mutex.WaitOne();

            // 5초간 대기 (연산이 처리되는 부분이라 가정)
            Thread.Sleep(5000);

            // 뮤텍스 해제
            mutex.ReleaseMutex();
        }
    }
}

우리가 사용하려는 메소드 안에 선언하여 사용하면 됩니다.

 

뮤텍스 사용의 또 다른 예

뮤텍스를 사용하여, 프로그램 중복 실행을 방지할 수 있습니다. 보통 뮤텍스의 이름을 기재할 때는 고유 GUID를 사용하여 명시하고, 프로그램이 이미 실행 중일 경우, 프로세서가 이미 존재하기 때문에 뮤텍스가 새로 할당되지 않습니다.

private static void Main(string[] args)
{
    Mutex mutex = new Mutex(false, "D8D34D71-CEBE-4970-AE2E-46C2EC86FEC9", out bool isCreate);  // 고유 GUID
    if (isCreate)
    {
        Console.WriteLine("프로그램 정상 실행");
    }
    else
    {
        Console.WriteLine("이미 프로그램이 실행 중입니다.");
    }
}

 

셰마포어(SemaPhore)


한 개의 자원을 동시 사용할 수 있게 하는 셰마포어

셰마포어는 하나의 자원을 동시에 여러 쓰레드가 사용할 수 있으나, 이 역시 순서가 존재합니다. 예를 들어 쓰레드1, 쓰레드2, 쓰레드3, 쓰레드4 ... 등등 여러 쓰레드가 있을 때, 셰마포어에서 "하나의 자원은 동시에 2개의 쓰레드가 사용할 수 있다."라고 규정을 한 경우, 2개의 쓰레드 중 먼저 끝나는 쓰레드가 생기면, 대기하고 있던 다음 쓰레드에게 순서대로 넘겨줍니다.

using System.Threading;

class Program
{
    private static void Main(string[] args)
    {
        TestClass testClass = new TestClass();
        for (int i = 0; i < 20; i++)
        {
            Thread thread = new Thread(testClass.Calculte);
            thread.Name = $"Thread ({i})";
            thread.Start();
        }
    }

    private class TestClass
    {
        //초기 실행 가능한 쓰레드 2개
        //최대 실행 가능한 쓰레드 2개
        Semaphore semaphore = new Semaphore(2, 2);

        public void Calculte()
        {
            // 셰마포어 취득할 때까지 기다린다.
            semaphore.WaitOne();

            // 1초간 대기 (연산이 처리되는 부분이라 가정)
            Thread.Sleep(5000);

            // 셰마포어 해제
            semaphore.Release();
        }
    }
}
반응형

댓글