Monitor에 대한 개념을 시작으로 중간에 Exit을 해주지않아 DeadLock이 발생하는 상황을 만들고, 더 간결하게 코드 작성을할 수 있는 lock 개념에 대해 배웠다.
아주 간단한 DeadLock의 원인이었으니, 더 복잡한 케이스를 통해 배워볼 차례이다.
Key를 두 개 가지고 있어야 들어갈 수 있는 화장실이 있다고 가정했을 때, 2개의 화장실 이용자(쓰레드)가 각 한개씩 Key를 가지게 될 경우
서로 양쪽 Key를 홀딩하고 있는 상황이라 다른 이용자가 가져가지도 못하고, 본인 또한 이용하지도 못하며 서로 아무것도 못하는 DeadLock이 발생한다.
간단한 해결 방법으로는 규칙을 만들어주면된다. KEY 1 부터 접근해야 한다는 조건을 걸어주면 누구던지 KEY 1에 접근하고, 키를 홀딩하고 있기에 다른 이용자가 접근하지 못하며
이용자 A의 경우가 KEY1,2를 모두 가져 화장실을 이용할 수 있게된다.
class PlaySection
{
static object key1 = new object();
public static void Test()
{
lock (key1)
{
UserSection.UserAction();
}
}
public static void PlayAction()
{
lock (key1)
{
}
}
}
class UserSection
{
static object key2 = new object();
public static void Test()
{
lock (key2)
{
PlaySection.PlayAction();
}
}
public static void UserAction()
{
lock (key2)
{
}
}
}
// 위 코드는 Task Start()를 통해 동작 시키고, 반복문에서 여러 번 작동하게 해둠
위와 같인 두 개의 Class에서 lock을 걸고, lock이 걸려 있는 다른 함수를 호출하는 과정을 서로 반복하며 위에서 언급했던 상황과 유사한 문제가 발생할 수 있다.
이해를 돕기 위한 설명
멀티 쓰레드 환경에서 보면PlaySection.Test()
와UserSection.Test()
가 거의 동시에 시작 되며 서로를 가지게 된다.
즉, 각자의 lock을 잡고 실행을 시키고자 하지만PlaySection.Test()
에서는UserSection.UserAction()
에 진입하고자 하는데 진입하기 위해서는 Key2가 필요하다.
근데 그 Key2는UserSection.Test()
가 잡고PlaySection.PlayAction()
에 진입하고자 한다. 이렇게 서로 lock 잡고 있어 다른 함수가 실행되지 못하는 상황에서 데드락이 발생하는 것이다.
1. Monitor.TryEnter(Object, Timespan)
과 같이 타임아웃을 이용해 일정시간 동안 통과하지 못할 경우, 다른 행동을 하도록 구현하는 방법
2. 접근할 때 일정 시간을 부여하는 것
사실 테스트 할 때 사용한 것과 같이 거의 동시에 쓰레드가 사용되는 경우는 잘 없다. 그렇기에 예시 처럼 거의 동시에 실행되고 있다면
Thread.Sleep()
으로 서로 잠시만 떨어지게 해두면 DeadLock 빠지지 않고 정상 종료 되는 걸 확인할 수 있다.
DeadLock은 QA중에도 잘 발생하지 않다가, 실제 트레픽이 늘어나며 원인을 발견하는 경우가 잦다고 한다. 미리 대비하는 것도 좋지만 어쩔 수 없는 경우에는 바로 원인을 파악하며 문제를 해결하는게 좋다고 할 수 있다.
아직 DeadLock 상황이 발생할 정도로 구조를 만들고 작업 하는 일이 없이 간단한 예제 케이스만 보니, 큰 흐름은 알겠지만 자세하게는 감이 안온다.
아무튼 실무에서도 자주 발생하는 원인들이니 잘 기억해두어야겠다.