데드락 (DeadLock)
데드락(Deadlock)은 컴퓨터 과학에서 여러 프로세스나 스레드가 서로를 무한히 기다리는 상태를 말한다. 이 상태에서는 모든 관련 프로세스나 스레드가 정지 상태에 빠져, 작업을 계속할 수 없게 된다.
ex) 화장실에는 두개의 자물쇠가 있다고 가정. 서로 다른 사람이 각각 하나의 자물쇠를 가지면 둘다 잠글 수 없는 데드락 상태가 발생한다. 그래서 1번 자물쇠부터 가지고, 2번 자물쇠를 가지도록 약속을 해야한다.
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)
{
}
}
}
internal 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(number);
}
}
}
이번에는 두개의 쓰레드에서 Lock을 사용하는 예제이다.
- Thread_1 : SessionManager.Test()를 호출 -> SessionManager의 락을 획득 -> 그 안에서 UserManager.TestUser()를 호출 -> UserManager.TestUser()는 UserManager의 락을 획득하려고 함
- Thread_2 : UserManager.Test()를 호출 -> UserManager의 락을 획득 -> 그 안에서 SessionManager.TestSession()을 호출 -> SessionManager.TestSession()는 SessionManager의 락을 획득하려고 함
이 시점에서 데드락이 발생할 수 있다. 만약 Thread_1이 SessionManager의 락을 획득하고 UserManager의 락을 기다리는 동안, Thread_2가 UserManager의 락을 획득하고 SessionManager의 락을 기다린다면, 두 스레드는 서로가 가진 락을 기다리며 무한정 대기하게 된다.
데드락 해결 방법
static void Main(string[] args)
{
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
Thread.Sleep(1000); // 시간차
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(number);
}
Lock을 획득하는 순서를 일관되게 유지할 수 있도록 Thread간 시간차를 준다.
※ 데드락은 개발하는 단계에서 발견되는 것 보다, 유저들이 많이 이용하는 라이브 서비스 중, 많이 발생한다. 데드락은 미리 예방하기보다는 크러쉬가 나면 그 후에 데드락이 발생한 위치를 찾고 문제를 해결하는 것이 더욱 간단할 수 있다.