운영체제
경쟁 상태·상호 배제·진행 보장
여러 스레드가 공유 상태를 다룰 때는 값의 정확성(상호 배제)과 모든 작업이 멈추지 않고 나아가는 성질(교착·기아 방지)을 함께 설계해야 합니다.
이럴 때 필요해요
잠금(Lock)을 추가했는데도 여전히 카운터 값이 틀리거나, 반대로 스레드들이 서로를 기다리느라 서버가 멈추는 장애를 막기 위해 필요합니다.
어떻게 동작하나요?
두 스레드가 동시에 공유 변수 count를 1씩 증가시켰는데 결과가 2가 아니라 1이 되는 현상을 경쟁 상태(Race Condition)라고 합니다. 코드에서는 한 줄로 보여도 실제로는 '현재 값 읽기 → 1 더하기 → 결과 기록하기'로 나뉘어 실행되기 때문에, 두 스레드가 동시에 0을 읽으면 둘 다 1을 덮어쓰며 한 번의 갱신이 증발해 버립니다. 따라서 불변식이 깨지지 않아야 하는 구간 전체를 임계 구역(Critical Section)으로 묶어 한 번에 한 흐름만 실행되게 보호해야 합니다.
이때 쓰는 대표적인 도구가 뮤텍스(Mutex)와 세마포어(Semaphore)입니다. 뮤텍스는 잠금을 획득한 소유자 스레드만 해제할 수 있는 상호 배제 잠금이고, 세마포어는 카운터를 두고 사용 가능한 자원 개수(예: 동시 DB 연결 수)를 제한하거나 실행 순서를 조율하는 도구입니다. 주의할 점은 개별 읽기·쓰기 함수 하나만 원자적으로 만들었다고 해서 '조건을 먼저 확인한 뒤 값을 변경하는(Check-then-Act)' 전체 논리까지 자동으로 안전해지지는 않는다는 것입니다.
잠금을 도입해 데이터 정확성을 지켰다면 그다음에는 작업들이 멈추지 않고 진행되는지 확인해야 합니다. 스레드 A는 잠금 1을 쥐고 잠금 2를 기다리고, 스레드 B는 잠금 2를 쥐고 잠금 1을 기다리면 아무도 진행하지 못하는 교착 상태(Deadlock)에 빠지는데, 이는 모든 스레드가 항상 같은 순서(잠금 1 → 잠금 2)로만 잠금을 잡게 규칙을 통일해 예방할 수 있습니다. 또한 다른 작업들은 잘 돌아가는데 우선순위가 낮은 특정 작업만 영원히 순서를 얻지 못하는 기아 상태(Starvation)도 공정한 큐나 에이징(Aging) 정책으로 함께 살핍니다.
코드 예제
동작을 설명하는 읽기 전용 예제입니다.
초기 count = 0
A: 읽기 0
B: 읽기 0
A: 1 기록
B: 1 기록
결과 1 (의도는 2)
해법: 같은 잠금으로 읽기~쓰기 전체를 보호두 실행 흐름 A와 B가 잠금 없이 동시에 count를 읽고 쓸 때 갱신 하나가 사라지는 과정을 추적한 예시입니다. 읽기부터 쓰기까지 전체 임계 구역을 같은 잠금으로 감싸야 의도한 결과 2를 얻습니다.
자주 헷갈리는 포인트
- 코드에 뮤텍스 잠금을 넣었다는 이유만으로 교착 상태(Deadlock)나 기아 상태(Starvation) 위험까지 없다고 안심하면 안 됩니다.
- 값을 수정하는 쓰기 경로만 잠금으로 감싸고, 그 값을 읽거나 조건을 검사하는 경로는 잠금 없이 방치하면 깨진 중간 상태를 읽을 수 있습니다.
내 말로 설명해 보기
두 스레드가 잠금 A와 잠금 B를 서로 반대 순서로 획득하려고 할 때 발생할 수 있는 교착 상태를 막으려면 팀에서 어떤 잠금 획득 규칙을 정해야 할까요?
실전 시나리오에 적용해 보기
함께 읽으면 좋은 개념
관련 면접 질문 4개
교착 상태(Deadlock)가 발생하는 네 가지 필요조건과 이를 예방하는 방법은 무엇인가요?
교착 상태는 실행 주체들이 서로 상대방이 점유한 자원을 기다리느라 아무도 진행하지 못하는 상태입니다. 고전적 자원 모델에서는 상호 배제, 점유와 대기, 비선점, 순환 대기라는 네 가지 필요조건이 동시에 성립해야 발생합니다. 이를 예방하려면 모든 스레드가 잠금(Lock)을 항상 정해진 같은 순서로만 획득하도록 강제해 순환 대기를 깨거나 필요한 자원을 한 번에 획득하는 방식을 쓰며, 이때 처리 효율과 구현 제약을 함께 고려합니다.
꼬리질문과 참고자료 →운영체제에서 기아 상태(Starvation)는 교착 상태(Deadlock)와 어떻게 다른가요?
기아 상태는 시스템 전체는 계속 진행하고 있지만, 특정 작업이 우선순위나 잠금 경쟁에서 계속 밀려 CPU나 자원을 끝내 얻지 못하는 현상입니다. 모두가 서로를 기다리느라 아예 멈춰 버리는 교착 상태(Deadlock)와 달리, 우선순위 스케줄링이나 불공정한 락에서 주로 발생합니다. 이를 완화하려면 대기 시간이 길어진 작업의 우선순위를 점진적으로 높이는 에이징(Aging)이나 선입선출 기반의 공정한 큐를 검토하며, 전체 처리량과 지연 시간의 절충을 함께 따집니다.
꼬리질문과 참고자료 →경쟁 상태(Race Condition)는 왜 발생하며 어떻게 재현하고 방지할 수 있나요?
경쟁 상태는 여러 실행 흐름이 공유 상태에 접근할 때 실행 타이밍(Interleaving)에 따라 결과의 정확성이 달라지는 현상입니다. 단순한 카운터 증가(count++)도 읽기·계산·쓰기 단계로 나뉘므로 중간에 다른 스레드가 끼어들면 갱신을 잃어버릴 수 있습니다. 이를 방지하려면 불변식이 유지되어야 하는 임계 구역(Critical Section)을 찾아 잠금(Lock), 원자 연산, 메시지 전달 등으로 보호하고 가능한 교차 실행 순서를 검증해야 합니다.
꼬리질문과 참고자료 →