프로그래밍/C#

[C#] Thread 동기화하기 Lock , Monitor

Victory_HA 2022. 4. 1. 00:19

Lock

  • C# 서비스 개발에 있어 비동기 프로그래밍은 필수적이라고 할 수 있다.
  • 멀티 쓰레드를 사용하여 리소스에 동시에 액세스하여 빠른 처리가 가능하다.
  • 한 개의 데이터를 여러개의 쓰레드가 동시에 접근, 변경하는 경우 Thread Unsafe하다고 말한다.
  • Lock키워드는 특정 블럭의 코드(Critical Section이라 부른다)를 한번에 하나의 쓰레드만 실행할 수 있도록 해준다.
  • 한번에 하나의 쓰레드만 사용하기 때문에 Thread Safe 하다고 말할 수 있다.
  • Critical Section은 가능한 한 범위를 작게하는 것이 좋다.
class SampleProgram
{
    private static object _lock = new object();
    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            new Thread(Dowork).Start();
        }

        Console.ReadLine();
    }

    public static void Dowork()
    {
        lock(_lock) // Critical Section 
        {
            Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} Start.");
            Thread.Sleep(2000);
            Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} End.");
        }
    }
}

출력 결과


Monitor

  • Monitor는 Lock과 동일한 기능을 하며, Critical Section을 만든다.
class SampleProgram
{
    private static object _lock = new object();
    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            new Thread(Dowork).Start();
        }

        Console.ReadLine();
    }

    public static void Dowork()
    {
        Monitor.Enter(_lock);   // Critical Section 시작

        Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} Start.");
        Thread.Sleep(2000);
        Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} End.");

        Monitor.Exit(_lock);    // Critical Section 끝
    }
}

  • Monitor는 다음과 Try-catch-finally 구문을 사용하여 예외 처리를 할 수 있다.

    class SampleProgram
    {
      private static object _lock = new object();
      static void Main(string[] args)
      {
          for (int i = 0; i < 5; i++)
          {
              new Thread(Dowork).Start();
          }
    
          Console.ReadLine();
      }
    
      public static void Dowork()
      {
          try
          {
              Monitor.Enter(_lock);   
              Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} Start.");
              Thread.Sleep(2000);
              Console.WriteLine($"Thread : {Thread.CurrentThread.ManagedThreadId} End.");
    
              throw new Exception();
          }
          catch(Exception e)
          {
              // Error log
          }
          finally
          {
              Monitor.Exit(_lock);
          }
      }
    }

출력 결과