1. 동기화(Synchronization) 문제란?
멀티프로세스 또는 멀티스레드 환경에서는 여러 개의 프로세스(또는 스레드)가 동시에 실행되면서 동일한 자원(변수, 파일, 메모리 등)에 접근할 수 있음. 이 과정에서 데이터 충돌, 무결성 문제, 경쟁 조건(Race Condition)이 발생할 수 있으며, 이를 "동기화(Synchronization) 문제"라고 함
✅ 동기화 문제가 발생하면?
- 데이터 손상: 두 개의 스레드가 동시에 같은 데이터를 수정하면, 예상하지 못한 값이 저장될 수 있음
- 일관성 문제: 여러 개의 프로세스가 같은 파일을 동시에 변경할 때, 올바르지 않은 데이터가 기록될 수 있음
- 무한 대기 상태: 프로세스가 서로의 작업이 끝나기를 기다리면서 교착 상태(Deadlock)에 빠질 수 있음
2. 동기화 문제의 대표적인 사례
예시 1: 은행 계좌 이체 문제
- A가 B에게 10만 원을 송금한다고 가정
- 두 개의 스레드가 동시에 잔액을 수정하려고 하면?
- 스레드 1: balance = balance - 100000 (A 계좌에서 차감)
- 스레드 2: balance = balance + 100000 (B 계좌에서 추가)
- 실행 순서가 꼬이면, 송금이 제대로 반영되지 않는 문제 발생
예시 2: 멀티스레딩 웹 서버의 문제
- 웹 서버에서 동시에 여러 사용자가 같은 파일을 다운로드한다고 가정
- 동시에 파일 크기를 변경하는 연산이 발생하면?
- 한 사용자의 파일 크기 계산이 끝나기 전에 다른 사용자의 파일이 덮어씌워질 가능성 있음
예시 3: 티켓 예매 시스템
- 두 명의 사용자가 동시에 마지막 남은 좌석을 예매하려고 하면?
- 스레드 1과 스레드 2가 동시에 check_available()을 실행 → 좌석이 있다고 판단
- 두 개의 스레드가 reserve_ticket()을 실행하면서 동일한 좌석이 두 번 판매될 가능성 발생
이러한 문제를 방지하기 위해 프로세스 및 스레드 간의 동기화 기법이 필요함
3. 임계 영역(Critical Section)이란?
멀티스레딩 환경에서 여러 개의 프로세스 또는 스레드가 공유 자원(파일, 변수, 데이터베이스 등)에 접근하는 특정 코드 영역을 **"임계 영역(Critical Section)"**이라고 한다.
임계 영역에서는 동시에 하나의 프로세스(또는 스레드)만 실행되어야 하며, 그렇지 않으면 데이터 무결성 문제나 경쟁 상태(Race Condition)가 발생할 수 있음
3-1. 임계 영역 문제 발생 예시
✅ 예제 1: 은행 계좌 잔액 업데이트 오류
- 사용자 A가 계좌에서 10만 원을 인출하는 요청을 보냄
- 사용자 B가 동시에 같은 계좌에서 5만 원을 입금하는 요청을 보냄
- 두 요청이 동시에 실행되면서, A의 요청이 B의 요청을 덮어써 버림
- 최종 잔액이 올바르게 반영되지 않는 오류 발생
✅ 예제 2: 온라인 쇼핑몰 재고 시스템
- 고객 A와 고객 B가 마지막 남은 상품을 동시에 구매하려고 함
- 두 개의 프로세스가 동시에 상품 재고를 확인하는 코드(임계 영역)를 실행
- 시스템이 두 고객 모두에게 상품이 있다고 응답
- 상품은 한 개뿐이므로, 한 명만 구매할 수 있어야 하지만, 중복 결제 오류 발생
이처럼 임계 영역을 보호하지 않으면, 시스템에서 심각한 데이터 오류가 발생할 수 있음
3-2. 임계 영역 문제 해결을 위한 조건
임계 영역을 올바르게 관리하려면 다음 3가지 조건을 반드시 만족해야 함
조건 | 설명 |
1️⃣ 상호 배제(Mutual Exclusion) | 한 번에 하나의 프로세스(또는 스레드)만 임계 영역을 실행할 수 있어야 함 |
2️⃣ 진행(Progress) | 다른 프로세스가 임계 영역을 사용하지 않는다면, 대기 중인 프로세스가 실행될 수 있어야 함 |
3️⃣ 유한 대기(Bounded Waiting) | 특정 프로세스가 무한히 기다리는 일이 없어야 하며, 공정하게 접근 기회를 가져야 함 |
✅ 이 조건이 충족되지 않으면?
- 서로 동시에 접근하면 "데이터 충돌" 발생 (상호 배제 X)
- 사용자가 요청을 했는데 실행되지 않는 "무한 대기 문제" 발생 (진행 조건 X)
- 어떤 프로세스는 계속 대기하고, 특정 프로세스만 실행되는 "기아 상태(Starvation)" 발생 (유한 대기 조건 X)
3-3. 임계 영역 문제를 해결하는 기법 (전통적인 기법)
임계 영역 문제를 해결하기 위해 다음과 같은 기본적인 기법들이 사용됨
1) 락(Lock) 기반 동기화
- 뮤텍스(Mutex), 세마포어(Semaphore)와 같은 동기화 도구를 사용하여 자원을 보호하는 방식
- 프로세스가 임계 영역에 진입하기 전에 Lock을 설정하고, 사용이 끝나면 Unlock을 수행
- 대표적인 기법: 뮤텍스(Mutex)와 세마포어(Semaphore) 활용
2) 번갈아 가며 실행하는 방식 (Turn Variable)
- 두 개의 프로세스가 번갈아 가며 실행할 수 있도록 공유 변수(Turn Variable)를 사용하여 접근을 조절
- 예
- 프로세스 A가 실행되면 turn = 1 설정
- 프로세스 B는 turn이 1이 될 때까지 대기
- A가 종료되면 turn = 2로 변경하여 B가 실행될 수 있도록 허용
3) 플래그(Flag) 기반 진입 허용 방식 (Peterson's Algorithm)
- Peterson 알고리즘은 두 개의 프로세스가 공유 자원에 동시에 접근하지 못하도록 제어하는 기법
- 각 프로세스는 자신이 임계 영역에 진입할 의사가 있음을 플래그로 설정하고,
상대 프로세스가 실행 중인지 확인하여 경쟁을 방지
int flag[2] = {0, 0};
int turn;
void process_A() {
flag[0] = 1; // 프로세스 A가 임계 영역에 진입하려 함
turn = 1; // 상대 프로세스에게 실행 권한을 줄 수 있도록 설정
while (flag[1] && turn == 1); // B가 실행 중이면 대기
// 임계 영역 (Critical Section)
printf("Process A is in the critical section\n");
flag[0] = 0; // 사용 완료 후 해제
}
Peterson 알고리즘의 특징
- 단순한 소프트웨어 기반 해결책
- 하지만 멀티코어 CPU 환경에서는 적절히 동작하지 않을 수도 있음 (메모리 일관성 문제 발생 가능)
3-4. 현대적인 임계 영역 문제 해결 기법
현대 운영체제에서는 더 강력하고 안정적인 동기화 도구를 활용하여 임계 영역 문제를 해결함
1) 스핀락(Spinlock) 활용
- Lock이 해제될 때까지 계속 루프를 돌면서 기다리는 방식
- 단순한 방식이지만, CPU 자원을 많이 소모하는 단점이 있음
- 멀티코어 환경에서 사용됨 (대기 시간이 짧은 경우 적합)
2) 조건 변수(Condition Variable) 활용
- 특정 조건이 만족될 때까지 스레드를 대기 상태로 유지할 수 있는 기법
- ex: 생산자-소비자 문제에서 버퍼가 가득 찰 경우 소비자가 소비할 때까지 생산자를 대기 상태로 만듦
3) 원자적 연산(Atomic Operation) 사용
- CPU에서 제공하는 "Atomic Instruction(원자 연산)"을 사용하여 데이터 경쟁을 방지
- 예: Compare-And-Swap(CAS), Fetch-And-Add 등의 기법
4) 트랜잭션 메모리(Transactional Memory) 활용
- 메모리 트랜잭션을 사용하여 동기화 문제를 해결하는 최신 기술
- 만약 트랜잭션이 실패하면 자동으로 롤백(Undo) 수행
4. 동기화 문제 해결 기법
멀티스레드 환경에서 동기화 문제를 해결하기 위해 세마포어(Semaphore), 뮤텍스(Mutex), 모니터(Monitor) 등의 기법이 사용됨
4-1. 뮤텍스(Mutex)란? (Mutual Exclusion Lock)
✅ 뮤텍스란?
- 한 번에 하나의 프로세스 또는 스레드만 특정 코드(임계 영역)에 접근하도록 제한하는 방법
- Lock(잠금)을 걸고, 사용이 끝나면 Unlock(해제)을 해야 함
📌 뮤텍스 사용 예시
pthread_mutex_t lock;
void critical_section() {
pthread_mutex_lock(&lock); // 뮤텍스 잠금
// 공유 자원 접근 (임계 영역)
pthread_mutex_unlock(&lock); // 뮤텍스 해제
}
✅ 뮤텍스의 특징
- 한 번에 한 개의 스레드만 접근 가능
- 다른 스레드는 대기 상태로 전환됨
- 자원을 사용한 후에는 반드시 잠금 해제(Unlock)해야 함
4-2. 세마포어(Semaphore)란?
✅ 세마포어란?
- 여러 개의 프로세스(또는 스레드)가 제한된 개수의 자원을 사용할 수 있도록 허용하는 기법
- 카운팅 방식으로 자원 접근을 제어함
- Binary Semaphore (이진 세마포어, 뮤텍스와 유사)
- Counting Semaphore (카운팅 세마포어, 여러 개의 프로세스를 허용)
📌 세마포어 사용 예시 (C 코드)
#include <semaphore.h>
sem_t semaphore;
void critical_section() {
sem_wait(&semaphore); // 자원 획득 (P 연산)
// 공유 자원 접근
sem_post(&semaphore); // 자원 반납 (V 연산)
}
✅ 세마포어의 특징
- 여러 개의 스레드가 동시에 실행될 수 있도록 허용 가능
- 리소스를 효율적으로 관리할 수 있음
- 다만, 세마포어를 잘못 설정하면 데드락(Deadlock)이 발생할 수도 있음
4-3. 모니터(Monitor)란?
✅ 모니터란?
- 뮤텍스 + 조건 변수(Condition Variable)를 함께 사용하는 방식
- 운영체제에서 언어 차원에서 제공되는 동기화 기법 (예: Java의 synchronized 블록)
📌 Java 모니터 사용 예시
class SharedResource {
synchronized void critical_section() {
// 공유 자원 접근 (임계 영역)
}
}
✅ 모니터의 특징
- 자바(Java)와 같은 객체지향 언어에서 사용
- 운영체제가 자동으로 Lock 및 Unlock을 관리
- 코드가 간결하지만, 성능 오버헤드가 발생할 수 있음
5. 동기화 문제 해결을 위한 선택 기준
기법 | 설명 | 적용 | 사례특징 |
뮤텍스(Mutex) | 한 번에 하나의 프로세스(스레드)만 접근 가능 | 파일 쓰기, 은행 계좌 업데이트 | Lock / Unlock 필요 |
세마포어(Semaphore) | 제한된 개수의 자원을 공유 | 티켓 예매 시스템, 네트워크 연결 | P/V 연산 사용, 다중 접근 가능 |
모니터(Monitor) | 뮤텍스 + 조건 변수 조합 | Java synchronized 블록 | 운영체제가 Lock 관리 |
'컴퓨터공학' 카테고리의 다른 글
페이지 교체 알고리즘 (FIFO, LRU, LFU, OPT)란? 개념과 비교 분석 (0) | 2025.03.07 |
---|---|
캐시 메모리(Cache Memory)와 성능 최적화 (0) | 2025.03.07 |
데드락(Deadlock)이란? 원인과 해결 방법 (0) | 2025.03.06 |
프로세스 스케줄링(Process Scheduling)이란? 선점형 vs 비선점형 스케줄링 비교 (0) | 2025.03.06 |
인터럽트(Interrupt)란? 하드웨어 인터럽트 vs 소프트웨어 인터럽트 (0) | 2025.03.06 |