멀티 스레드 환경에서 같은 객체를 여러 곳에서 호출하는 경우
예기치 않은 결과가 나타날 확률이 높다.
📌 Monitor 활용
상호 배제(Mutual Exclusive)를 이뤄 다른 스레드의 접근을 막도록 한다.
Enter과 Exit로 구현하며 Enter는 문을 잠그는 행위, Exit는 문을 여는 행위라 생각하면 된다.
Thread 1에서 Monitor.Enter(_obj)를 통해 임계 영역(Critical Section)에 들어가면,
Thread2는 Thread 1에서 Monitor.Exit(obj)로 해제할 때 까지 해당 영역에 접근하지 못한다.
*임계 영역 : 여러 스레드가 공유 자원에 접근할 때, 하나만 접근할 수 있도록 보장해 주는 영역.
static int number = 0; //공유 자원
static object _obj = new object(); //좌물쇠 역할
static void Thread_1()
{
for (int i = 0; i < 100000; i++)
{
/*상호배체 Mutual Exclusive */
Monitor.Enter(_obj); //문을 잠그는 행위 -> 다른 이 들어올 수 없음
number++;
Monitor.Exit(_obj); //문을 다시 엶 -> 다른 이 접근 가능
}
}
static void Thread_2()
{
for (int i = 0; i < 100000; i++)
{
Monitor.Enter(_obj);
number--;
Monitor.Exit(_obj);
}
}
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);
}
}
만약, Moniter.Exit를 통해 문을 열지 않고 나가버리면 어떤 상황이 발생할까..
👉 데드락(DeadLock)이 발생한다.
📌 DeadLock이란?
무한히 다음 자원을 기다리는 상태.
상대방의 작업이 끝나기 만을 기다리고 있는 상태로 결과적으로 아무것도 완료되지 못하는 상태이다.
static int number = 0; //공유 자원
static object _obj = new object(); //좌물쇠 역할
static void Thread_1()
{
for (int i = 0; i < 100000; i++)
{
/*상호배체 Mutual Exclusive */
Monitor.Enter(_obj);
{
number++;
return; //문을 열지 않고 그냥 빠져나감
}
Monitor.Exit(_obj);
}
}
//무한 대기 상태로 DeadLock이 발생함.
static void Thread_2()
{
for (int i = 0; i < 100000; i++)
{
Monitor.Enter(_obj);
number--;
Monitor.Exit(_obj);
}
}
위와 같이 return을 해버리면 Exit로 빠져나오지 못하고 계속 잠겨있는 상태가 된다.
그러면 Thread 2는 열리기만을 기다리는 상태로 무한 대기에 빠져 DeadLock이 발생한 것이다.
💡 해결 방법?
▶ try finally로 해결할 수 있다.
finally를 통해 해당 구문을 반드시 한번은 수행하기 때문이다.
try
{
Monitor.Enter(_lock);
return;
}
finally
{
Monitor.Exit(_lock);
}
하지만 코드가 길어지고 복잡해 질 가능성이 있다..
가장 좋은 방법은 lock을 사용하는 것이다.
📌 lock 이란?
크리티컬 섹션을 하나의 스레드만 실행할 수 있도록 해주는 것
다음과 같이 구현하면 된다.
static void Thread_1()
{
for (int i = 0; i < 100000; i++)
{
lock(_obj)
{
number++;
}
}
}
static void Thread_2()
{
for (int i = 0; i < 100000; i++)
{
lock(_obj)
{
number--;
}
}
}
안전한 스레드를 만들기 위해 Lock 방법을 사용하는 것을 가장 추천한다.
'게임 서버 > Thread' 카테고리의 다른 글
[c#/유니티] ReaderWriterLock 구현 하기 (0) | 2022.12.09 |
---|---|
[c#/유니티] DeadLock과 SpinLock에 대해 (0) | 2022.12.08 |
[c#/유니티] Interlocked에 대해 (2) | 2022.12.07 |
[c#/유니티] 캐시(Cache) 이론과 메모리 배리어(Memory Barrier) (2) | 2022.12.06 |
[c#/유니티] 컴파일러 최적화 (1) | 2022.12.06 |