게임서버/멀티쓰레드 프로그래밍
데드락
코다람쥐
2022. 3. 10. 21:30
1. 자물쇠가 2개인 경우
1번쓰레드가 1번자물쇠를 가지고있고
2번쓰레드가 2번자물쇠를 가지고있다고 가정했을 때
1번쓰레드가 1번자물쇠를 가진상태에서 2번자물쇠에 접근하려고 대기하고,
2번쓰레드가 2번자물쇠를 가진상태에서 1번자물쇠에 접근하려고 대기하면 교착상태가 발생한다.
위의 데드락을 방지하기 위해서는 쓰레드끼리 1번자물쇠부터 먼저 접근하고 그 다음에 2번자물쇠에 접근하게끔 설정해주면된다. 그러면 먼저 도착한 쓰레드가 1번자물쇠를 사용하고 락을 해제하면 다음에 도착한 쓰레드가 1번을 사용하게 되기 때문에 교착상태가 발생하지 않는다.
코드로 살펴보면 아래와 같다.
using System;
using System.Threading;
using System.Threading.Tasks;
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)
{
}
}
}
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("빠져나옴!");
}
}
}
실제로 코드를 실행해보면 교착상태에 빠지는 것을 알 수 있다.
2. 해결방법
방법1. Monitor.TryEnter
Monitor.TryEnter(_lock, 100);
0.1초동안 락을 획득하지 못하면 다시 돌아오는 방법이다.
그런데 애초에 교착상태가 발생했다는 것은 구조적으로 문제가 있는 것이기 때문에 그냥 새롭게 고치는 방법을 권유한다.
방법2. Thread.Sleep()
static void Main(string[] args)
{
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
Thread.Sleep(100); // 0.1초 후 실행
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine("빠져나옴!");
}
동시에 락을 서로 획득하려고 하면 교착상태가 발생하기 때문에 0.1초의 시간차를 줘서 데드락을 막는 방법이다.
사실 미리 방지하는 방법은 별로 없고 그냥 크래시가 날때마다 데드락을 확인하는 방법이 유일하다.