ABOUT ME

Today
Yesterday
Total
  • 데드락
    게임서버/멀티쓰레드 프로그래밍 2022. 3. 10. 21:30

    1. 자물쇠가 2개인 경우

     

    1번쓰레드가 1번자물쇠를 가지고있고

    2번쓰레드가 2번자물쇠를 가지고있다고 가정했을 때

     

    1번쓰레드가 1번자물쇠를 가진상태에서 2번자물쇠에 접근하려고 대기하고,

    2번쓰레드가 2번자물쇠를 가진상태에서 1번자물쇠에 접근하려고 대기하면 교착상태가 발생한다.

    데드락발생

     

    위의 데드락을 방지하기 위해서는 쓰레드끼리 1번자물쇠부터 먼저 접근하고 그 다음에 2번자물쇠에 접근하게끔 설정해주면된다. 그러면 먼저 도착한 쓰레드가 1번자물쇠를 사용하고 락을 해제하면 다음에 도착한 쓰레드가 1번을 사용하게 되기 때문에 교착상태가 발생하지 않는다.

     

    코드로 살펴보면 아래와 같다.

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ServerCore
    {
        class SessionManager
        {
            static object _lock = new object();
            public static void TestSession()
            {
                lock (_lock)
                {
    
                }
            }
            public static void Test()
            {
                lock (_lock)
                {
                    UserManager.TestUser();
                }
            }
        }
    
        class UserManager
        {
            static object _lock = new object();
            public static void Test()
            {
                lock (_lock)
                {
                    SessionManager.TestSession();
                }
            }
    
            public static void TestUser()
            {
                lock (_lock)
                {
    
                }
            }
        }
    
        class Program
        {
            static int number = 0;
            static object _obj = new object();
            
            static void Thread_1()
            {
                for (int i = 0; i < 10000; i++)
                {
                    SessionManager.Test();
                }            
            }
    
            static void Thread_2()
            {
                for (int i = 0; i < 10000; i++)
                {
                    UserManager.Test();
                }
            }
            static void Main(string[] args)
            {
                Task t1 = new Task(Thread_1);
                Task t2 = new Task(Thread_2);
                t1.Start();
                t2.Start();
    
                Task.WaitAll(t1, t2);
    
                Console.WriteLine("빠져나옴!");
            }
        }
    }

    실제로 코드를 실행해보면 교착상태에 빠지는 것을 알 수 있다.

     

    2. 해결방법

    방법1. Monitor.TryEnter

    Monitor.TryEnter(_lock, 100);

    0.1초동안 락을 획득하지 못하면 다시 돌아오는 방법이다.

    그런데 애초에 교착상태가 발생했다는 것은 구조적으로 문제가 있는 것이기 때문에 그냥 새롭게 고치는 방법을 권유한다.

     

    방법2. Thread.Sleep()

    static void Main(string[] args)
            {
                Task t1 = new Task(Thread_1);
                Task t2 = new Task(Thread_2);
                t1.Start();
    
                Thread.Sleep(100); // 0.1초 후 실행
    
                t2.Start();
    
                Task.WaitAll(t1, t2);
    
                Console.WriteLine("빠져나옴!");
            }

    동시에 락을 서로 획득하려고 하면 교착상태가 발생하기 때문에 0.1초의 시간차를 줘서 데드락을 막는 방법이다.

     

    사실 미리 방지하는 방법은 별로 없고 그냥 크래시가 날때마다 데드락을 확인하는 방법이 유일하다.

    '게임서버 > 멀티쓰레드 프로그래밍' 카테고리의 다른 글

    스핀락(SpinLock)  (0) 2022.03.11
    Lock 구현 이론  (0) 2022.03.11
    Lock 기초  (0) 2022.03.10
    Interlocked  (0) 2022.03.10
    메모리 베리어  (0) 2022.03.10
Designed by Tistory.