데드락이란?

정의

  • 두 개 이상 스레드가 서로의 자원 해제를 기다리며 영원히 진행하지 못하는 상태입니다.
  • CPU 사용률이 낮아도 서비스는 멈출 수 있어, "조용한 장애"로 나타나는 경우가 많습니다.

비슷한 문제와 구분

문제특징
Deadlock서로 기다리며 영구 정지
Livelock계속 움직이지만 진전 없음
Starvation특정 스레드만 계속 기아 상태

데드락의 4조건 (Coffman)

4조건

조건의미
상호 배제자원을 동시에 하나만 사용 가능
점유 대기자원 하나를 가진 채 다른 자원 대기
비선점강제로 자원 회수 불가
순환 대기A가 B를, B가 C를, C가 A를 기다림

핵심 원리

  • 이 네 가지가 동시에 성립할 때 데드락이 가능합니다.
  • 예방은 "4조건 중 최소 하나를 깨는 설계"라고 이해하면 쉽습니다.

대표 재현 시나리오

락 순서 역전

std::mutex m1, m2;

void T1() {
    std::lock_guard<std::mutex> g1(m1);
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::lock_guard<std::mutex> g2(m2);
}

void T2() {
    std::lock_guard<std::mutex> g1(m2);
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
    std::lock_guard<std::mutex> g2(m1);
}

왜 멈추는가

  • T1은 m1을 쥔 채 m2를 기다리고,
  • T2는 m2를 쥔 채 m1을 기다립니다.
  • 서로 풀어주길 기다리는 순환 대기가 완성됩니다.

예방 패턴 1 - 락 순서와 RAII

락 순서 규칙

  • 팀/프로젝트 차원에서 전역 락 순서(계층) 를 정합니다. (예: AccountLock -> InventoryLock -> WorldLock)
  • 모든 코드 경로가 이 순서를 따르도록 강제하면 순환 대기를 크게 줄일 수 있습니다.

RAII로 누락 방지

  • 수동 lock()/unlock() 대신 lock_guard, unique_lock을 기본으로 사용합니다.
  • 예외/조기 반환 경로에서도 unlock 누락을 막을 수 있습니다.

예방 패턴 2 - 표준 라이브러리 활용

std::scoped_lock (권장)

std::mutex m1, m2;

void Safe() {
    std::scoped_lock lock(m1, m2);  // 교착 회피 방식으로 동시 획득
    // 임계 영역
}

std::lock + adopt_lock

std::lock(m1, m2);
std::lock_guard<std::mutex> g1(m1, std::adopt_lock);
std::lock_guard<std::mutex> g2(m2, std::adopt_lock);
  • 복수 락 획득 로직을 직접 작성하지 말고 표준 도구를 우선 사용하세요.

예방 패턴 3 - try_lock/timeout과 복구

타임아웃 기반 방어

std::timed_mutex m;

bool TryWork() {
    if (!m.try_lock_for(std::chrono::milliseconds(10))) {
        return false;  // 재시도/롤백/로그
    }
    std::lock_guard<std::timed_mutex> lock(m, std::adopt_lock);
    // 작업
    return true;
}

실무 적용 포인트

  • 온라인 서버는 "무한 대기"보다 "실패 후 복구" 전략이 운영상 안전한 경우가 많습니다.
  • 단, 타임아웃은 데드락을 "숨길" 수 있으므로 원인 추적 로그를 반드시 남겨야 합니다.

탐지와 디버깅 체크리스트

관측 신호

  • 요청 처리량이 갑자기 0에 가까워짐
  • CPU는 낮은데 대기 스레드 수가 증가
  • 특정 락 근처 스택 프레임이 여러 스레드에서 반복

점검 절차

  1. 스레드 덤프/콜스택 수집
  2. 각 스레드의 "보유 락"과 "대기 락"을 표로 정리
  3. 락 그래프에서 순환(Cycle) 존재 여부 확인
  4. 해당 경로를 scoped_lock/순서 통일로 수정

강의 시 유의사항

강조 포인트

  • 데드락은 재현이 어려워 "코드 리뷰 단계 예방"이 가장 중요합니다.
  • "락 순서 통일 + RAII + 표준 도구 사용"이 실무에서 가장 효과적입니다.

자주 하는 오해

오해바로잡기
unlock 누락만 고치면 데드락은 끝락 순서 역전만으로도 데드락 발생 가능
try_lock을 쓰면 데드락이 사라진다회피 확률을 높일 뿐 근본 원인 제거는 별개
데드락은 테스트에서 바로 잡힌다타이밍 의존이라 라이브에서만 드러나기 쉬움

체크 질문 (스스로 답해보기)

  • Coffman 4조건을 실제 코드 예시에 대응시켜 설명할 수 있는가?
  • 두 mutex를 잡아야 할 때 scoped_lock이 왜 안전한가?
  • timeout 기반 회피를 도입할 때 필수로 남겨야 하는 운영 로그는 무엇인가?

profile
李家네_공부방

0개의 댓글