C#의 교착 상태(Deadlock)란?
교착 상태(Deadlock)는 한 스레드가 특정 리소스를 잠근(lock) 상태에서, 다른 스레드가 동시에 그 리소스를 요구할 때 발생합니다. 이 문제는 멀티프로세싱 환경에서 매우 빈번하게 나타나며, 두 개 이상의 스레드가 서로가 소유한 리소스를 무한정 기다리면서 프로그램 전체가 멈추는 현상을 의미합니다.
다음은 가장 대표적인 교착 상태 발생 예시입니다.
| 스레드 1 | 스레드 2 |
|---|---|
| 락 P 획득 | 락 Q 획득 |
| 락 Q 요청 | 락 P 요청 |
위 예제에서 스레드 1은 락 Q를 얻지 못합니다. 락 Q는 이미 스레드 2가 소유하고 있기 때문입니다. 마찬가지로 스레드 2 역시 락 P를 얻을 수 없는데, 락 P의 원래 소유자가 스레드 1이기 때문입니다. 결과적으로 두 스레드는 서로가 점유한 락을 영원히 기다리게 되어 교착 상태에 빠집니다.
교착 상태는 반드시 두 스레드 사이에서만 일어나는 것은 아닙니다. 세 개의 스레드와 세 개의 락이 얽혀 있으면 3방향(three-way) 교착 상태가 발생할 수 있으며, 같은 원리로 4방향, 5방향 등 더 복잡한 형태의 교착 상태도 충분히 가능합니다.
기아 상태(Starvation)란?
기아 상태(Starvation)는 멀티스레드 애플리케이션에서 실행 가능한(runnable) 하나 이상의 스레드가 영구적으로 차단되어 프로세서 시간을 배정받지 못하는 현상을 말합니다. 주로 우선순위가 낮은 스레드가 우선순위가 높은 스레드들에 계속 밀려 실행 기회를 얻지 못할 때 발생하며, 교착 상태와 달리 락이 해제되지 않는 것이 아니라 스케줄링 과정에서 소외되는 것이 핵심 차이점입니다.
교착 상태 예방 팁
- 여러 락을 획득할 때는 항상 일관된 순서를 유지합니다.
Monitor.TryEnter처럼 타임아웃을 활용해 무한 대기를 방지합니다.- 락을 보유한 상태에서의 작업 시간을 최소화합니다.
- 공유 객체에 대한 중첩 잠금(nested locking)은 가급적 피합니다.