Home [멀티쓰레드] DeadLock
Post
Cancel

[멀티쓰레드] DeadLock

DeadLock

DeadLock(데드락, 교착상태)이란 두 개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태를 가리킨다.

DeadLock 상황을 간단하게 만들어보자.

예를 들어 SessionManager와 User들을 관리하는 UserManager 두 클래스가 있다. 멀티쓰레드 환경에서 돌아가다보니 각 클래스에서는 lock을 하나씩 쥐고 있고 서로 호출할 일이 있다고 해보자.

SesstionManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SessionManager
{
    static object _lock = new object();

    public static void TestSession()
    {
        lock(_lock) // 다른 쓰레드는 못들어오게 lock을 걸고 작업한다.
        {

        }
    }

    public static void Test()
    {
        lock(_lock) // 다른 쓰레드는 못들어오게 lock을 걸고 작업한다.
        {
            UserManager.TestUser(); // UserManager 호출
        }
    }
}

UserManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class UserManager
{
    static object _lock = new object();

    public static void TestUser()
    {
        lock(_lock) // 다른 쓰레드는 못들어오게 lock을 걸고 작업한다.
        {

        }
    }

    public static void Test()
    {
        lock(_lock) // 다른 쓰레드는 못들어오게 lock을 걸고 작업한다.
        {
            SessionManager.TestSession(); // SessionManager 호출
        }
    }
}

위 처럼 간단히 정의한 다음에 두 클래스를 서로 다른 쓰레드로 수행시키면 어떻게 될까.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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("쓰레드 무사히 종료");
}

실행시켜보면 알겠지만 쓰레드가 종료되지 않는 것을 볼 수 있을 것이다.

위 상황을 해석해보면 서로 자신의 lock을 걸어둔채로 상대방의 lock이 풀릴때까지 하염없이 기다리고 있는 상황이다.

DeadLock 출처: DeadLock 이란

이처럼 서로 상대방의 작업이 끝나기만을 하염없이 기다리고 있는 상태를 교착상태 혹은 DeadLock이라고 한다.

DeadLock은 보통 실행시켜보기 전까진 발견되기 쉽지않다. 그렇다보니 대부분의 경우 실행시켜보고 실제로 발생한 다음에 고치는 경우가 많다. 그만큼 DeadLock이 까다롭고 관리하기 쉽지 않다는 것이다.

DeadLock 해결방법

DeadLock을 해결하는 방법은 여러가지가 있지만 사실 완전히 막는 것은 힘들다. 그렇다보니 대부분의 방법이 예방 또는 방지하는 방법이다. 간단하게만 살펴보자면 아래와 같다.

DeadLock 예방

DeadLock이 발생하려면 아래 4가지 조건을 모두 만족해야한다.

  1. 상호 배제(Mutual exclusion): 자원은 한 번에 한 프로세스만이 사용할 수 있어야 한다.
  2. 점유 대기(Hold and wait): 최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용하고 있는 자원을 추가로 점유하기 위해 대기하는 프로세스가 있어야 한다.
  3. 비선점(No preemption): 다른 프로세스에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없어야 한다.
  4. 순환 대기 (Circular wait): 프로세스의 집합 {P0, P1, ,…Pn}에서 P0는 P1이 점유한 자원을 대기하고 P1은 P2가 점유한 자원을 대기하고 P2…Pn-1은 Pn이 점유한 자원을 대기하며 Pn은 P0가 점유한 자원을 요구해야 한다.

이 조건 중에서 한 가지라도 만족하지 않으면 DeadLock은 발생하지 않는다. 이 점을 이용하여 4가지 조건중 한가지를 막음으로써 DeadLock이 발생하지 않도록 예방하는 것이다. 다만 이 방법은 자원 사용의 효율성이 떨어지고 비용이 많이 드는 문제점이 있다.

DeadLock 회피

자원이 어떻게 요청될지에 대해 사전에 검사하여 회피하는 방법이다.

교착 상태 회피하기 위한 알고리즘으로 크게 두가지가 있다.

  1. 자원 할당 그래프 알고리즘 (Resource Allocation Graph Algorithm)
  2. 은행원 알고리즘 (Banker’s algorithm)

DeadLock 무시

위 예방 혹은 회피기법을 사용하기에는 비용이 크다보니 성능에 큰 영향을 미칠 수 있게 된다. 그렇기 때문에 데드락의 발생 확률이 비교적 낮은 경우 별다른 조치를 취하지 않는 경우도 있다.

DeadLock 탐지 및 회복

DeadLock을 탐지하는 알고리즘을 넣어 DeadLock이 발생했는지를 탐지하고 DeadLock이 발생했을 경우 회복하는 방법이다. 이 역시 성능에 큰 영향을 미칠 수 있다.

This post is licensed under CC BY 4.0 by the author.