경쟁 상태(Race Condition)
- 여러 프로세스나 스레드가 동시에 공유 자원에 접근하려고 할 때 발생하는 문제
- 접근 순서에 따라 프로그램의 동작이 달라질 수 있는 상황
- 운영체제나 병렬 처리 시스템에서 흔히 발생하는 문제 중 하나
경쟁 상태의 발생 조건
- 공유 자원 : 여러 프로세스나 스레드가 동시에 접근할 수 있는 자원(메모리, 파일, 데이터베이스 등)이 있어야 함
- 동시성 : 여러 프로세스나 스레드가 동시에 실행되며, 이들이 공유 자원에 접근하고 할 때 발생
- 비결정적 순서 : 프로세스나 스레드가 자원에 접근하는 순서가 보장되지 않으며, 이 순서 따라 결과가 달라질 수 있음
경쟁 상태의 예시
- 두 개의 스레드가 같은 변수에 접근하여 값을 수정하는 경우
int counter = 0;
void increment() {
counter = counter + 1;
}
void thread1() {
increment();
}
void thread2() {
increment();
}
- 'thread1'이 'counter' 값을 읽고, 1을 더하려고 함
- 그 사이에 'thread2'도 동일하게 'counter' 값을 읽고, 1을 더하려고 함
- 'thread1'과 'thread2' 모두 'counter' 값을 1 증가시켜야 하지만, 실제로는 두 스레가 동시에 값을 수정하기 때문에 'counter'의 값이 2가 아닌 1로 증가할 수 있음
경쟁 상태의 해결
- 경쟁 상태를 해결하기 위해서는 동기화 기법이 필요
- 뮤텍스(Mutex) : 스레드 간의 자원 접근을 제어해야 할 때 사용
- 동작 원리
- 잠금(Lock) : 스레드가 공유 자원에 접근하려면 먼저 뮤텍스를 잠가야 함. 다른 스레드는 이 자원이 잠겨 있는 동안 접근할 수 없음
- 해제(Unlock) : 자원 사용이 끝나면 뮤텍스를 해제하여 다른 스레드가 접근할 수 있도록 함
- 특징
- 이진 상태 : 뮤텍스는 기본적으로 이진 상태. 한 번에 하나의 스레드만 자원에 접근할 수 있음
- 소유권 : 뮤텍스는 잠금을 요청한 스레드만이 잠금을 해제할 수 있음

- 세마포어(Semaphore) : 특정 자원에 대해 여러 스레드가 동시에 접근할 수 있지만, 그 수를 제한하고자 할 때 사용
- 동작 원리
- 카운터 값 : 세마포어는 내부적으로 카운터 값을 유지. 이 카운터 값을 현사용할 수 있는 자원의 수를 나타냄
- P 연산(Wait/Decrement) : 자원에 접근하기 전에 세마포어의 카운터 값을 감소시킴. 카운터 값이 0보다 크다면 자원에 접근할 수 있으며, 그렇지 않으면 대기
- V 연산(Signal/Increment) : 자원 사용이 끝난 후 세마포어의 카운터 값을 증가시킴. 이를 통해 다른 대기 중인 스레드나 프로세스가 자원에 접근할 수 있게 됨
- 특징
- 이진 세마포어 : 뮤텍스처럼 동작하며, 카운터 값이 0 또는 1. 한 번에 하의 스레드만 자원에 접근할 수 있음
- 카운팅 세마포어 : 카운터 값이 N 이상으로 설정될 수 있으며, 최대 N개의 스레드가 동시에 자원에 접근할 수 있음
- 소유권 없음 : 세마포어는 잠금과 해제의 소유권 개념이 없음. 잠근 스레가 아닌 다른 스레드도 해제를 수행할 수 있음

- 모니터(Monitor) : 객체 지향 언어에서 자주 사용되는 개념으로, 객체의 메서드들이 동기화되어 있어 여러 스레드가 접근하지 못하게 함
참고
https://hudi.blog/race-condition-critical-section-mutual-exclusion/
https://heeonii.tistory.com/14