데드락
자물쇠를 2개다 획득해야만 화장실에 들어갈 수 있다고 가정.해보자.
두 사람이 있다면 각각 사람이 1개씩 자물쇠를 가지고 그 다음 자물쇠를 가질려고 할 때 이미 한 개씩은 가지고 있으므로 모두 자물쇠를 하나씩 더 획득하는 것이 불가능하게 된다.
이와 같은 상황이 발생하는 이유는 자물쇠를 잠그는 순서가 안맞기 때문이다.
A사람은 1번자물쇠를 먼저 잠그고 B사람은 2번 자물쇠를 먼저 잠그기 때문에 서로 사이클이 일어나는 문제가 발생한다.
왜 화장실은 한개인데 자물쇠를 2개 사용하는 상황이 일어날까?
코드상으로 확인
class SessionManager
{
static object _lock = new object();
public static void TestSession()
{
lock (_lock)
{
}
}
static void Test()
{
lock(_lock)
{
UserManager.TestUser();
}
}
}
class UserManager
{
static object _lock = new object();
public static void TestUser()
{
lock(_lock)
{
}
}
static void Test()
{
lock(_lock)
{
SessionManager.TestSession();
}
}
}
각각의 Thread에서 하나는 SessionManager의 Test를 호출하고 또 다른 하나는 UserManager를 호출하다 보면 꼬이는 현상이 일어나게 된다.
결과를 확인해보자
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("DeadLock아님!");
}
코드를 실행시키면
DeadLock이 걸린 것을 확인해 볼 수 있다.
실행 과정
1. Thread_1과 Thread_2 실행 준비
2. Thread_1이 SessionManager.Test() 메서드에 진입하려고 SessionManager의 _lock개체에 lock을 요쳥한다.
3. Thread_2이 UserManager.Test()메서드에 진입하려고 UserManager의 _lock 객체에 lock을 요청한다. 하지만 _lock개체는 이미 Thread_1에 의해 잠겨있으므로 대기한다.
4. Thread_1이 SessionManager.Test() 메서드 내부에서 UserManager.TestUser() 메서드를 호출한다. 이때 _lock객체를 요청하려고 하지만 이미 Thread_1에 의해 잠겨있으므로 대기 상태에 들어간다. Thread_1과 Thread_2가 서로 상대의 lock를 기다리면서 대기 상태로 빠지게 되어 데드락이 발생한다.
Monitor.TryEnter를 통해서 문제를 해결할 수 있지 않을까?
Monitor.TryEnter를 통해서 내가 몇초동안 시도를 해보다가 락을 획득하는데 실패하면 깔끔하게 포기하겠다. -> 이를 실제 코드에 사용하게되면 코드 락 구조 자체가 문제가 있기 때문에 이 또한 문제가 된다.
결론
데드락이 일어나게 되면 그 상황에 맞춰서 고치는 것이 제일 좋다. 데드락은 예방하기 힘들지만 이유를 찾아 고치는 것이 더 쉬울 수 있다.
데드락은 막상 개발단계에서 안일어날 수 있는데 라이브 상황에서 유저들이 몰릴때 터지는 경우가 많다.
대부분 Session과 User가 동시에 따단 실행하는 경우가 드물다. User가 접속할 때 user와 관련된 부분이 실행되고 usersession이랑 관련된 부분이 있을때 실행된다.
예방 방법
01 시작 시간 겹치지 않게 하기
그러므로 띄엄띄엄 일어난다고 가정을 해서 Thread.Sleep을 넣게 된다.
t1.Start();
Thread.Sleep(100);
t2.Start();
DeadLock이 아님을 확인할 수 있다.
정확하게 둘이 일치한 타이밍에 실행을 하는 경우에만 맞물리는 현상이 발생하게 된다. 조금만 시작 시간이 어긋나게 되면 DeadLock이 발생하지 않는 것을 확인할 수 있다.
02 Lock 미리 맵핑하기(fair locking)
각각 id를 하나씩 부여한다. 그리고 실행을 한 후 내가 lock을 잡고 있는 상태에서 다른 애를 실행하고 내가 갖고 있는 애 보다 id가 높으면은 문제가 있는 것이다. 그리고 바로 crash낼 수 있다.
참고 : 본 내용은 MMORPG PART4 강의를 수강하여 작성하였습니다.
https://www.inflearn.com/course/%EC%9C%A0%EB%8B%88%ED%8B%B0-mmorpg-%EA%B0%9C%EB%B0%9C-part4
'cs공부 > 운영체제' 카테고리의 다른 글
운영체제 - SpinLock(스핀락) - Test-and-Set, Compare-and-Swap (0) | 2023.05.22 |
---|---|
운영체제 - C# Lock구현 연습 (0) | 2023.05.22 |
운영체제 - Peterson's Solution, Bakery 알고리즘(상호배제 문제 해결) (0) | 2023.05.16 |
운영체제 - 임계구역,동기화(Monitor,try-finally,lock) (0) | 2023.05.16 |
운영체제 - 프로세스 시스템 (0) | 2023.05.16 |
댓글