A race condition occurs when the correctness of a program depends on the relative timing or ordering of concurrent operations.
Race Condition은 두 개 이상의 실행 흐름이 동시에 동일한 공유 자원에 접근할 때 실행 순서에 따라 프로그램 결과가 달라지는 현상을 의미한다.
즉 프로그램의 동작이 스케줄링, 실행 타이밍, CPU 인터리빙에 의존하게 되는 상황이다.
Race Condition이 발생하려면 일반적으로 다음 세 조건이 필요하다.
여러 실행 흐름이 존재해야 한다.
두 실행 흐름이 동일한 자원에 접근해야 한다.
공유 자원을 수정하는 연산이 Atomic하지 않을 때 Race Condition이 발생한다.
counter++
이 연산은 실제로 CPU에서 다음 단계와 같이 실행된다.
1. load counter
2. add 1
3. store counter
이 사이에 다른 스레드가 개입할 수 있다.
멀티스레드 프로그램에서는 모든 스레드가 같은 주소 공간을 공유한다.
반면 각 스레드는 Stack만 따로 가진다.
따라서 전역 변수 또는 heap 데이터에 동시에 접근하면 Race Condition이 발생할 수 있다.
운영체제 스케줄러는 스레드를 매우 짧은 시간 단위로 번갈아 실행한다.
가 있다면 실행 흐름은
와 같이 실행 순서가 인터리빙(Interleaving)된다.
Race Condition은 이 인터리빙 순서에 의해 발생한다.
멀티코어 CPU에서는 Race Condition이 더 쉽게 발생한다.
두 코어가 동시에 실행하기 때문이다.
또한 Memory Consistency 문제도 발생한다.
각 CPU는 캐시를 사용하지만 캐시 때문에 메모리 값이 즉시 동기화되지 않을 수 있다.
Race Condition이 발생하는 코드를 Critical Section이라고 한다.
Critical Section은 공유 자원에 접근하는 코드 영역을 의미한다.
counter++
예를 들어 이 연산은 공유 변수 counter를 수정하므로 임계 구역에 된다. 여러 스레드가 동시에 이 영역을 실행하면 Race Condition이 발생할 수 있다.
따라서 임계 구역은 동시에 하나의 실행 흐름만 접근해야 한다.
운영체제 이론에서는 Critical Section 문제를 해결하기 위해 다음 세 조건을 만족해야 한다.
한 번에 하나의 스레드만 임계 구역에 들어갈 수 있어야 한다.
임계 구역이 비어 있을 때 어떤 스레드가 들어갈지 결정할 수 있어야 한다.
특정 스레드가 임계 구역에 들어가기 위해 무한히 기다리면 안된다. (기아상태)
Race Condition은 두 개 이상의 실행 흐름이 동시에 동일한 공유 자원에 접근할 때 실행 순서나 타이밍에 따라 프로그램의 결과가 달라지는 현상을 의미한다. 이는 멀티스레드나 멀티코어 환경에서 특히 자주 발생하고 여러 스레드가 전역 변수나 힙 메모리와 같은 공유 데이터를 동시에 수정할 때 연산이 원자적으로 수행되지 않으면 문제가 발생한다. 이러한 상황은 운영체제 스케줄러에 의해 실행 흐름이 인터리빙 되면서 나타나고 공유 자원에 접근하는 코드 영역을 임계 구역이라고 한다. Race Condition을 방지하기 위해 여러 동기화 기법을 사용하여 한 번에 하나의 실행 흐름만 해당 자원에 접근하도록 제어할 수 있다.