class SessionManager
{
static object _lock_session = new object();
public static void TestSession()
{
lock(_lock_session)
{}
}
public static void Test()
{
lock (_lock_session)
{
UserManager.TestUser();
}
}
}
class UserManager
{
static object _lock_user = new object();
public static void TestUser()
{
lock(_lock_user)
{}
}
public static void Test()
{
lock(_lock_user)
{
SessionManager.TestSession();
}
}
}
class Program
{
static int number = 0;
static object _obj = new object();
static void Thread_1()
{
for(int i=0; i<100; i++)
{
SessionManager.Test();
}
}
static void Thread_2()
{
for(int i=0; i<100; 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("데드락에 걸리지 않았다!");
}
}
Thread_1 에서 SessionManger.Test를 호출할 때 _lock_session을 사용해서 Lock을 걸고
이때 동시에 Thread_2 는 UserManger.Test를 호출해서 _lock_user를 사용해서 Lock을 건다.
Thread_1은 SessionManger.Test를 통해 UserManger.TestUser를 호출할 때 _lock_user로 Lock을 걸어야 한다.
근데 Thread_2에서 _lock_user를 사용하고 있다보니까 Lock을 걸 수 없고
마찬가지로, Thread_2 역시 UserManger.Test를 통해 SessionManager.TestSession을 호출할 때
_lock_session으로 Lock을 걸어야 하는데
Thread_1에서 이미 사용하고 있다보니 Lock을 걸 수 없다 보니
데드락에 빠지게 된다.
번외로, 데드락이 나면 예외처리로 고치기 보다는 차라리 크래쉬를 내는 것이 더 좋다.
Lock 구조에 문제기 있어 데드락이 발생한거기 때문에 그 상황에서 예외처리를 하는 것은 장기적인 관점에서 좋지 않다. 훗날 어떤 예외가 튀어 나올지 모르기 대문이다.
또 데드락이 무서운 이유는 조금의 시간차가 있으면 발생하지 않을 수 있기 때문이다.
위 코드에서
t1.Start();
Thread.Sleep(100);
t2.Start();
t1과 t2의 시작 시간을 다르게 해주면 코드가 정상적으로 작동하는 것을 볼 수 있다.
이것은 임시적인 방인이다
반응형
'프로그래밍(Programming) > C#' 카테고리의 다른 글
컨텍스트 스위칭을 발생시키는 명령 Thread.Sleep 0 , 1 , yield (0) | 2022.11.27 |
---|---|
Interlocked.CompareExchange(ref _lockecd, desired, expected) (0) | 2022.11.27 |
lock 과 Monitor 임계영역 (0) | 2022.11.26 |
Interlocked.Increment 과 Race Condition (0) | 2022.11.26 |
MemoryBarrier 와 연산 순서/가시성 (0) | 2022.11.25 |