두 개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태를 가리킨다.
class SessionManager
{
static object _lock = new object();
public static void TestSession()
{
lock (_lock) // t2 쓰레드가 열쇠가 없어서 못들어가는 곳
{
}
}
public static void Test()
{
lock(_lock)
{
UserManager.TestUser();
}
}
}
class UserManager
{
static object _lock = new object();
public static void TestUser()
{
lock(_lock) // t1 쓰레드가 열쇠가 없어서 못들어가는 곳
{
}
}
public static void Test()
{
lock(_lock)
{
SessionManager.TestSession();
}
}
}
internal class Program
{
static void Thread1()
{
for (int i = 0; i < 100000; i++)
SessionManager.Test();
}
static void Thread2()
{
for (int i = 0; i < 100000; i++)
UserManager.Test();
}
static void Main(string[] args)
{
Task t1 = new Task(Thread1);
Task t2 = new Task(Thread2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
}
}
각각의 클래스에 존재하는 _lock 변수를 열쇠라고 생각하고, lock(_lock) 부분을 문고리라고 생각하자.
위 코드에서 두 개의 쓰레드가 각각 SessionManager와 UserManager 클래스의 Test() 함수를 호출하고 있다. 먼저 Thread1(즉 t1 쓰레드)가 SessionManager의 집에 있는 _lock이라는 하나밖에 없는 열쇠를 집어들고 lock()이라는 문고리의 열쇠구멍에 넣어서 lock(_lock)이 되었다고 생각하자. 그러면 열쇠가 열쇠구멍에 넣어졌기 때문에 문이 열릴 것이고 UserManager.TestUser()를 호출할 것이다. 이때 t1 쓰레드와 동시에 t2 쓰레드도 t1와 똑같은 행동을 할 것이기 때문에 결과적으로 t2도 SessionManager.TestSession()를 호출할 것이다.
하지만 t1이 이미 SessionManager의 _lock이라는 열쇠를 가져가서 열쇠구멍에 쑤셔넣어 문을 여는데 사용을 했기 때문에(현재 SessionManager의 Test() 함수에 있는 열쇠구멍에 꽂혀있는 상태임) 열쇠가 없어서 TestSession() 함수의 lock(_lock) 부분만 멍하니 쳐다보게 될 것이다. (t1도 이미 t2가 열쇠를 써버렸기 때문에 없는건 마찬가지)
아래는 내가 데드락을 잘 이해하고 있는 것이 맞는지 테스트한 코드이다. 동시에 들어가서 일어난 문제이니 호출횟수를 10회로 줄이고 t1이 열쇠를 다 쓰고 반납한 후에 시간을 좀 두고 t2 쓰레드를 동작시켰다.