데드락(Deadlock)은 두 개 이상의 프로세스나 스레드가 서로가 보유한 자원을 기다리면서 영원히 대기 상태에 빠지는 문제를 의미합니다. 즉, 서로가 서로를 기다리기 때문에 아무도 앞으로 진행하지 못하는 상태라고 볼 수 있습니다.
멀티 스레드 환경에서 자원 잠금을 잘못 처리하면 쉽게 발생할 수 있는 대표적인 동시성 문제입니다.
데드락은 아래 4가지 조건이 모두 만족해야 발생합니다.
상호 배제(Mutual Exclusion)
점유와 대기(Hold and Wait)
비선점(No Preemption)
순환 대기(Circular Wait)
이 중 하나라도 깨면 데드락을 방지할 수 있습니다. (중요)
아래 예시는 데드락이 실제로 발생하는 C++ 코드입니다.
ThreadA와 ThreadB가 서로 반대로 mutex를 잠금으로써 데드락이 발생합니다.
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;
mutex m1;
mutex m2;
void ThreadA() {
cout << "[ThreadA] Lock m1\n";
m1.lock();
this_thread::sleep_for(chrono::milliseconds(100));
cout << "[ThreadA] Try Lock m2\n";
m2.lock(); // ThreadB가 m2를 잡고 있으므로 데드락
}
void ThreadB() {
cout << "[ThreadB] Lock m2\n";
m2.lock();
this_thread::sleep_for(chrono::milliseconds(100));
cout << "[ThreadB] Try Lock m1\n";
m1.lock(); // ThreadA가 m1을 잡고 있으므로 데드락
}
int main() {
thread t1(ThreadA);
thread t2(ThreadB);
t1.join();
t2.join();
return 0;
}

이 상태에서 프로그램이 더 이상 진행되지 않았습니다.
모든 스레드가 항상 같은 순서(A → B)로 자원을 잠그도록 합니다.
이렇게 하면 순환 대기 조건을 깨뜨릴 수 있습니다.
=> AB/BA 금지
std::scoped_lock 사용하기 (C++17 이상)std::scoped_lock lock(m1, m2);
두 mutex를 데드락 없이 안전하게 한 번에 잠글 수 있습니다.
try_lock 사용자원을 못 획득하면 아예 포기하거나 재시도하여 무한 대기를 피합니다.