[Server] #15-8. 데드락

데드락

Deadlock(교착상태)이란 여러 개의 스레드가 서로 상대방이 점유한 락을 기다리면서 무한 대기 상태에 빠진 현상을 말한다. 즉, 두 개 이상의 스레드가 서로 상대방이 가진 자원을 해제할 때까지 기다리기 때문에 프로그램이 멈춰버리는 문제이다.


발생 원인

데드락이 발생하려면 4가지 조건이 모두 성립해야 한다.

  1. 상호 배제(Mutual Exclusion)
    • 한 번에 한 스레드만 자원을 사용할 수 있어야 한다.
  2. 점유 대기(Hold and Wait)
    • 스레드가 이미 하나의 자원을 점유한 상태에서 추가로 다른 자원을 기다리는 상황이다.
  3. 비선점(No Preemption)
    • 다른 스레드가 점유한 자원을 강제로 빼앗을 수 없어야 한다.
  4. 순환 대기(Circular Wait)
    • 여러 스레드가 사이클 구조로 자원을 점유한 상태에서 서로의 자원을 기다리고 있어야 한다. - 이 네 가지 조건이 모두 충족될 때 데드락이 발생할 수 있다.

데드락 실습

다음은 데드락이 발생하는 예제이다.

std::mutex m1;	// Account Manager
std::mutex m2;	// User Manager

void Thread_1()
{
	for (int i = 0; i < 10000; i++)
	{
		std::lock_guard<std::mutex> lockGuard1(m1);
		std::lock_guard<std::mutex> lockGuard2(m2);
	}
}

void Thread_2()
{
	for (int i = 0; i < 10000; i++)
	{
		std::lock_guard<std::mutex> lockGuard1(m2);
		std::lock_guard<std::mutex> lockGuard2(m1);
	}
}

int main()
{
	std::thread t1(Thread_1);
	std::thread t2(Thread_2);

	t1.join();
	t2.join();

	std::cout << "Jobs Done" << std::endl;
}
  • 분석
    • Thread_1은 m1을 먼저 잡고 m2를 잡으려고 한다.
    • Thread_2는 m2를 먼저 잡고 m1을 잡으려고 한다.
    • Thread_1은 Thread_2가 m2를 해제할 때까지 기다리고, Thread_2는 Thread_1이 m1을 해제할 때까지 기다린다.
    • 두 스레드가 서로의 락을 기다리면서 무한 대기 상태(데드락)에 빠진다.

데드락 해결 방법

  • 락 획득 순서 통일하기
    • 락을 항상 일정한 순서로 획득하면 데드락을 예방할 수 있다.
    • Thread_2의 락 획득 순서를 Thread_1과 동일하게 변경하면, 순환 대기 조건이 깨져 데드락이 발생하지 않는다.
void Thread_2()
{
	for (int i = 0; i < 10000; i++)
	{
		std::lock_guard<std::mutex> lockGuard1(m1);
		std::lock_guard<std::mutex> lockGuard2(m2);
	}
}

추가 설명

  • 디버깅
    • 위의 예제는 스레드가 2개라서 단순한 경우지만, 멀티 스레드 환경에서는 락을 잡는 순서가 복잡해지면서 문제 해결이 어려워진다.
    • 버그를 찾는 건 쉬울 수 있지만, 시스템 구조가 복잡할수록 전체적인 영향을 고려해야 하기 때문에 고치는 건 어려울 수 있다.
  • 개발 vs 라이브 환경
    • 개발 단계에서는 문제 없이 실행 됐는데, 라이브 환경에서는 데드락이 발생할 수 있다. → 접속자가 늘어날 경우. 즉, 테스트 환경에서는 쉽게 재현되지 않을 수 있다.
    • 예제에서도 for문의 반복 횟수를 줄이면 데드락이 발생하지 않을 수 있다.
  • 데드락 탐지법
    • 락 구조를 그래프로 표현하고, 사이클이 존재하는지 확인한다.
  • 결론
    • 데드락의 근본적인 원인은 락을 잡는 순서가 꼬이는 것이다. 락을 일정한 순서로 잡으면 데드락을 예방할 수 있다.
    • 하지만 완벽하게 처리하는 방법은 존재하지 않는다. 규모가 작은 프로젝트에서는 데드락이 거의 발생하지 않지만, 대규모 시스템에서는 데드락 탐지 및 우회 기법을 사용해 우회하기도 한다.

출처 Rookiss님 게임 프로그래머 입문 올인원

Categories:

Updated:

Leave a comment