unreal 5기

251112 언리얼엔진 본캠프 65일차 데드락(Deadlock)

parkjinnam 2025. 11. 12. 20:57

데드락(Deadlock, 교착상태)

데드락은 멀티스레딩 환경에서 두 개 이상의 스레드가 서로 점유하고 있는 자원을 기다리며 무한 대기 상태에 빠지는 현상을 말한다. 예컨대 A스레드는 B스레드가 점유하고 있는 변수에 접근하기 위해 B스레드가 해당 변수의 점유를 해제하는 것을 기다리고 있는데 B스레드 또한 A스레드가 점유하고 있는 변수에 접근하기 위해 기다리고 있는 서로가 서로를 기다리면서 먼저 점유 해제하라고 쳐다보고 있는 상황이 되시겠다. std::Mutex처럼 한 번에 하나의 스레드만 자원을 사용하도록 하는 상호 배제와, 하나의 자원을 점유한 스레드가 다른 자원을 추가로 요청하며 대기하는 점유 및 대기, 다른 스레드가 점유한 자원을 강제로 뺏을 수 없는 비선점, 스레드들이 원형으로 서로의 자원을 기다리는 형태의 순환대기의 4가지 현상이 모두 발생하면 데드락이 발생하게 된다.

 

데드락의 해결방법

앞서 데드락은 상호배제, 점유 및 대기, 비선점, 순환대기의 4가지 조건이 동시에 충족되어야만 발생한다고 했다. 즉 4가지 조건중 하나라도 무력화한다면 데드락을 해결할 수 있다는 얘기이기도 하다. 데드락의 해결방법으로는 크게 네 가지가 있다.

 

1. 락 순서의 강제

순환 대기 조건을 깨트리는 방법으로서 여러 개의 뮤텍스를 잠글 때 모든 스레드가 항상 동일한 순서로 락을 획득하도록 규칙을 정하는 것이다. 예컨대 뮤텍스 A, B가 있다고 했을 때 규칙으로 무조건 A를 먼저 잠그고 그다음 B를 잠근다는 규칙을 정하게 되면

 

Thread 1 : lock(A); lock(B);

 

Thread 2 : lock(A); lock(B);

위의 상황에서 Thread 2는 1이 A를 놓을 때까지 기다렸다가 잠그게 되므로 데드락이 발생하지 않게 된다.

 

2. std::scoped_lock(C++ 17)이나 std::lock(C++ 11)의 사용

위의 방법은 점유 및 대기 조건을 깨트리는 방법으로서 여러 개의 뮤텍스를 동시에 잠가야 할 때 사용한다.

 

std ::scoped_lock : 여러 뮤텍스를 인자로 받아 데드락을 피하는 알고리즘을 사용해서 모든 뮤텍스를 한 번에 잠근다. RAII를 지원하여 매우 편리하지만 C++ 17 이상에서 권장된다.

 

std::lock : std::scoped_lock와 유사하게 여러 뮤텍스를 데드락 없이 잠그는 함수이다만 RAII를 지원하지 않기에 std::unique_lock과 함께 사용해야 한다.

 

3. try_lock의 사용 및 대처

비선점 조건을 완화하거나 점유 및 대기 조건을 깨는 방법으로서 std::mutex::try_lock() 함수는 뮤텍스를 즉시 잠글 수 있으면 true를 아니면 대기하지 않고 즉시 false를 반환하게 된다. 두 번째 락을 잡으려 할 때 try_lock을 사용하고 만약 실패한다면 이미 잡고 있던 첫 번째 락을 스스로 해제한 뒤 잠시 대기했다가 처음부터 다시 시도하는 방식이다. 이는 사용이 복잡해서 2번의 std::scoped_lock을 사용하지 못할 경우에 사용한다.

 

4. 락 범위 최소화

데드락을 직접 해결하는 방법은 아니지만 데드락 확률을 줄이고 코드를 안전하게 만드는 기본 원칙으로서 말 그대로 코딩 습관을 들이자는 얘기이다. 락은 필요한 최소한의 시간 동안만 점유해야 하며 std::lock_guard 또는 std::scoped_lock를 사용해 RAII 패턴을 따르는 것이 좋다.