[임계 영역이란]
둘 이상의 스레드, 즉 멀티 스레드 환경에서 여러 스레드가 동시에 같은 메모리 영역에 접근하게 되면 메모리에 저장된 데이터의 무결성을 훼손할 수 있습니다. 이러한 영역에서는 경쟁 상태(Race Condition)가 발생할 수 있으므로 상호 배제(Mutual Exclusion)가 보장되어야하는데, 이러한 공간을 임계 영역이라고 합니다.
[발생 예시]
아래의 코드는 임계 영역 문제가 발생할 수 있는 코드입니다.
#include <iostream>
int32 sum = 0;
void Add()
{
for (int32 i = 0; i < 1'000'000; i++)
{
sum++;
}
}
void Sub()
{
for (int32 i = 0; i < 1'000'000; i++)
{
sum--;
}
}
int main()
{
thread temp(Add); // 꿀팁팁 : Ctrl + D 하면 바로 아래줄에 코드 복붙
thread temp2(Sub);
temp.join();
temp2.join();
cout << sum << endl;
}
두 스레드가 sum이라는 변수에 대해 Add와 Sub을 실시하는 코드입니다. sum은 두 스레드가 접근 가능한 메모리 영역에 존재하며, sum++과 sum--은 한 줄의 코드로 보입니다. 하지만 코드를 어셈블리어로 확인하면 단계가 아래와 같이 나눠집니다.
00007FF7924C2436 mov eax, dword ptr[sum(07FF7924CF470h)]
00007FF7924C243C inc eax
00007FF7924C243E mov dword ptr[sum(07FF7924CF470h)], eax
1. 어떤 주소(데이터 영역의 sum을 가리키는 중)에 있는 값을 eax 레지스터에 넣어라
2. eax 레지스터에 있는 값을 1 증가시켜라
3. 그 결과물을 다시 eax 레지스터에 넣어라
위의 코드를 sum++에 의사 코드로 적용해보면, Add() 함수는 아래와 같은 형태가 됩니다.
void Add()
{
for (int32 i = 0; i < 1'000'000; i++)
{
// sum++;
int32 eax = sum;
eax = eax + 1;
sum = eax;
}
}
CPU는 한 번에 작업을 수행하는 것이 불가능하게 설계되어 있습니다. 고로 값을 꺼내오는 동시에 연산을 처리할 수가 없어 Add()와 Sub()이 sum이라는 값에 병렬 처리를 하면 다음과 같은 시나리오가 발생할 수 있습니다.
Add와 Sub 함수가 병렬처리 되는 과정을 살펴보자.
1. 데이터 영역의 sum은 현재 0
2. Add() 호출 시 int eax = sum이 실행되면서 sum의 값 0이 eax에 들어감
3. 마침 Sub()도 동시에 int eax = sum을 실행하면서 eax가 0이 됨
4. Add()가 다음 로직을 먼저 수행한다고 하면, eax = eax + 1을 실행
5. 그리고 연산된 eax 값을 sum에 넣으면 sum은 1이 됨
6. 이때 Sub()도 연산을 마치고 sum에 값을 넣으면 1에 값을 덮어쓰면서 -1이 됨
7. 이 과정이 반복될 경우 숫자가 의도대로 계산되지 않고 꼬이게 됨
[해결 방법]
임계 영역 문제를 해결하기 위해서는 특수한 처리를 필요로 합니다(보통 하나의 스레드가 일을 처리할 경우, 다른 스레드가 해당 영역에 접근하지 못하도록 하는 방식).
1. 유저 모드 동기화
- 크리티컬 섹션 기반의 동기화
- 인터락 함수 기반의 동기화
2. 커널 모드 동기화
- 뮤텍스 기반 동기화
- 세마포어 기반 동기화
- 이벤트 기반의 동기화
'컴퓨터과학 > 시스템' 카테고리의 다른 글
| [CS] 가상 메모리(Virtual Memory) (0) | 2025.05.18 |
|---|---|
| [CS] 데드락(Dead Lock) (0) | 2025.05.18 |
| [CS] 커널(Kernel) (0) | 2025.05.13 |
| [CS] 메모리(Memory) (0) | 2025.03.21 |
| [CS] 시스템 콜(System Call) (0) | 2025.03.21 |