🖥️ [컴퓨터구조] 병렬성과 동기화 문제
2️⃣ 명령어와 컴퓨터 언어
컴퓨터의 성능을 향상시키는 주요 방법 중 하나는 병렬 처리(Parallel Processing) 를 활용하는 것입니다.
멀티코어 CPU, 멀티스레드 프로그래밍, 분산 시스템에서는 여러 개의 작업을 동시에 수행하여 성능을 극대화할 수 있습니다.
그러나, 병렬 처리에는 동기화(Synchronization) 문제, 데이터 경쟁(Race Condition), 교착 상태(Deadlock) 등의 복잡한 문제가 발생할 수 있습니다.
이번 섹션에서는 병렬 처리의 개념, 동기화 문제의 원인과 해결 방법, 그리고 실제 사례에서의 적용 방법을 자세히 살펴보겠습니다.
🔹 1. 병렬성(Parallelism)이란?
✅ 1.1 병렬 처리(Parallel Processing)의 개념
병렬 처리란 여러 개의 프로세서 또는 스레드가 동시에 연산을 수행하는 방식을 의미합니다.
✅ 병렬 처리의 목적:
- 연산 속도 향상 → 더 많은 작업을 짧은 시간에 처리
- 시스템 자원 활용 극대화 → CPU 코어, GPU, 분산 시스템 활용
- 대규모 데이터 처리 → AI, 과학 연산, 시뮬레이션 등에 필수
💡 병렬 처리는 CPU의 여러 코어가 동시에 연산을 수행하는 방식!
✅ 1.2 병렬 처리의 종류
병렬 처리는 하드웨어 구조 및 실행 방식에 따라 여러 유형으로 나뉩니다.
병렬 처리 모델 | 설명 | 예제 |
SISD (단일 명령어, 단일 데이터) | 전통적인 순차적 실행 방식 | 일반적인 싱글코어 CPU |
SIMD (단일 명령어, 다중 데이터) | 동일한 연산을 여러 데이터에 수행 | 벡터 연산, AVX, GPU |
MISD (다중 명령어, 단일 데이터) | 하나의 데이터를 여러 방식으로 처리 | 신경망, 파이프라인 |
MIMD (다중 명령어, 다중 데이터) | 여러 연산이 동시에 실행 | 멀티코어 CPU, 클러스터 컴퓨팅 |
💡 실제 사례:
- SIMD (Single Instruction, Multiple Data) → 그래픽 처리, AI 연산
- MIMD (Multiple Instruction, Multiple Data) → 멀티코어 프로세서, 데이터센터
🔹 2. 병렬 처리의 문제점 (동기화 문제)
✅ 2.1 동기화(Synchronization) 문제란?
병렬 처리는 여러 개의 프로세스 또는 스레드가 동시에 같은 자원(데이터)에 접근할 때 문제가 발생할 수 있습니다.
이러한 문제를 해결하지 않으면 데이터 불일치, 성능 저하, 예측 불가능한 결과가 발생할 수 있습니다.
문제 유형 | 설명 |
경쟁 조건(Race Condition) | 두 개 이상의 스레드가 동일한 데이터를 동시에 수정하려 할 때 발생 |
데이터 일관성(Data Consistency) 문제 | 여러 스레드가 공유 데이터를 다룰 때 값이 엉킬 수 있음 |
교착 상태(Deadlock) | 여러 프로세스가 서로 자원을 기다리면서 영원히 멈추는 상태 |
기아 상태(Starvation) | 특정 프로세스가 계속해서 CPU 자원을 할당받지 못하는 문제 |
💡 쉽게 말해:
- 여러 개의 스레드가 동시에 같은 데이터를 수정하면 데이터가 엉킬 수 있음!
- 동기화 기법을 적용해야 문제를 해결할 수 있음!
✅ 2.2 동기화 문제의 실제 예제
예제: 경쟁 조건 (Race Condition)
💡 두 개의 스레드가 동일한 변수(count)를 증가시키는 코드
int count = 0;
void thread1() {
count = count + 1; // count 값을 증가
}
void thread2() {
count = count + 1; // 동시에 count 값을 증가
}
✅ 문제:
- count 값이 1 증가해야 하지만,
- 두 개의 스레드가 동시에 접근하면 예상치 못한 결과가 발생할 수 있음.
💡 해결 방법:
- 뮤텍스(Mutex), 세마포어(Semaphore), 원자적 연산(Atomic Operation) 등을 사용하여 동기화 필요
🔹 3. 동기화(Synchronization) 기법
동기화 문제를 해결하기 위해 다음과 같은 기법이 사용됩니다.
✅ 3.1 락(Lock) 기반 동기화
🔹 1) 뮤텍스(Mutex, Mutual Exclusion)
- 하나의 스레드만 공유 자원에 접근할 수 있도록 락(Lock)을 설정
- 락을 획득한 스레드만 연산을 수행할 수 있음
💡 뮤텍스 예제 (C - Pthread)
pthread_mutex_t lock;
int count = 0;
void* thread_function(void* arg) {
pthread_mutex_lock(&lock); // 락 획득
count++;
pthread_mutex_unlock(&lock); // 락 해제
}
✔ 장점: 데이터 충돌 방지 가능
❌ 단점: 성능 저하(락을 획득하는 동안 다른 스레드는 대기)
🔹 2) 세마포어(Semaphore)
- 여러 개의 스레드가 동시에 접근할 수 있도록 제한하는 방식
- 데이터베이스, 네트워크 서버 등에서 많이 사용됨
💡 세마포어 예제 (C - Pthread)
sem_t semaphore;
sem_wait(&semaphore); // 접근 시도
count++;
sem_post(&semaphore); // 접근 해제
✔ 장점: 뮤텍스보다 더 유연한 접근 방식 제공
❌ 단점: 복잡한 관리가 필요
✅ 3.2 원자적 연산(Atomic Operations)
- CPU가 하드웨어적으로 락을 사용하지 않고 동기화할 수 있도록 지원
- CPU 명령어 자체가 원자적(Atomic)으로 실행됨
💡 원자적 연산 예제 (C - GCC Built-in Function)
__sync_fetch_and_add(&count, 1); // count 값을 원자적으로 증가
✔ 장점: 락 없이 빠른 동기화 가능
❌ 단점: 모든 연산을 원자적으로 수행할 수 없음
✅ 3.3 교착 상태(Deadlock) 해결 방법
교착 상태(Deadlock)가 발생하면 두 개 이상의 스레드가 서로 자원을 점유한 채 무한 대기하는 상태가 됩니다.
이를 방지하기 위한 기법은 다음과 같습니다.
해결 방법 | 설명 |
자원 할당 순서(Resource Ordering) | 자원을 항상 동일한 순서로 할당 |
타임아웃 설정(Timeouts) | 일정 시간 후에 락을 해제 |
교착 상태 탐지(Deadlock Detection) | 주기적으로 교착 상태를 감지하여 해결 |
✅ 마무리: 병렬성과 동기화 문제의 핵심
✅ 병렬 처리는 성능을 극대화하지만, 동기화 문제가 발생할 수 있음
✅ 경쟁 조건(Race Condition), 데이터 불일치 문제 해결 필요
✅ 뮤텍스(Mutex), 세마포어(Semaphore), 원자적 연산(Atomic)으로 동기화 문제 해결 가능
✅ 교착 상태(Deadlock) 발생 방지를 위한 전략 필요