728x90
반응형
SMALL
생산자-소비자 문제 🧩
생산자-소비자 문제는 공유 자원(Buffer)를 통해 데이터를 주고받는 대표적인 동기화 문제예요.
- 생산자: 데이터를 생성해서 버퍼에 추가하는 역할
- 소비자: 버퍼에서 데이터를 꺼내 사용하는 역할
문제는 생산자와 소비자가 동시에 버퍼에 접근하면 충돌이 발생할 수 있다는 점이에요.
이를 해결하기 위해 뮤텍스(Mutex)와 세마포어(Semaphore)를 사용해요.
동작 원리 🛠️
- 버퍼(Buffer): 데이터를 저장하는 공간. 버퍼가 꽉 차면 생산자는 대기하고, 버퍼가 비면 소비자는 대기해야 해요.
- 세마포어 사용
empty
: 버퍼의 빈 공간 개수를 관리.full
: 버퍼의 채워진 데이터 개수를 관리.
- 뮤텍스 사용: 버퍼에 접근할 때 한 번에 하나의 스레드만 접근하도록 보장.
자바 예제 ☕
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
public class ProducerConsumerExample {
// 공유 자원 (버퍼)
private final LinkedList<Integer> buffer = new LinkedList<>();
private final int maxSize = 5; // 버퍼 최대 크기
// 세마포어와 뮤텍스
private final Semaphore empty = new Semaphore(maxSize); // 빈 공간 추적
private final Semaphore full = new Semaphore(0); // 채워진 데이터 추적
private final Semaphore mutex = new Semaphore(1); // 버퍼 접근 제어
// 생산자
public void produce() throws InterruptedException {
for (int i = 0; i < 10; i++) {
empty.acquire(); // 빈 공간 확인
mutex.acquire(); // 버퍼 접근 잠금
// 데이터 생성 및 추가
buffer.add(i);
System.out.println("Produced: " + i);
mutex.release(); // 버퍼 잠금 해제
full.release(); // 채워진 데이터 증가
Thread.sleep(500); // 생산 속도 제어
}
}
// 소비자
public void consume() throws InterruptedException {
for (int i = 0; i < 10; i++) {
full.acquire(); // 채워진 데이터 확인
mutex.acquire(); // 버퍼 접근 잠금
// 데이터 소비 및 제거
int item = buffer.removeFirst();
System.out.println("Consumed: " + item);
mutex.release(); // 버퍼 잠금 해제
empty.release(); // 빈 공간 증가
Thread.sleep(1000); // 소비 속도 제어
}
}
public static void main(String[] args) {
ProducerConsumerExample pc = new ProducerConsumerExample();
// 생산자와 소비자 스레드 생성
Thread producer = new Thread(() -> {
try {
pc.produce();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
pc.consume();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
파이썬 예제 🐍
import threading
import time
import random
# 공유 자원 (버퍼)
buffer = []
max_size = 5
# 세마포어와 뮤텍스
empty = threading.Semaphore(max_size) # 빈 공간 추적
full = threading.Semaphore(0) # 채워진 데이터 추적
mutex = threading.Lock() # 버퍼 접근 제어
# 생산자
def produce():
for i in range(10):
empty.acquire() # 빈 공간 확인
with mutex: # 버퍼 접근 잠금
buffer.append(i) # 데이터 생성 및 추가
print(f"Produced: {i}")
full.release() # 채워진 데이터 증가
time.sleep(random.uniform(0.5, 1.0)) # 생산 속도 제어
# 소비자
def consume():
for i in range(10):
full.acquire() # 채워진 데이터 확인
with mutex: # 버퍼 접근 잠금
item = buffer.pop(0) # 데이터 소비 및 제거
print(f"Consumed: {item}")
empty.release() # 빈 공간 증가
time.sleep(random.uniform(1.0, 1.5)) # 소비 속도 제어
# 생산자와 소비자 스레드 생성
producer = threading.Thread(target=produce)
consumer = threading.Thread(target=consume)
producer.start()
consumer.start()
producer.join()
consumer.join()
동작 설명 🎯
- 생산자(produce)는 데이터를 생성해서 버퍼에 추가해요.
- 버퍼가 꽉 차면
empty.acquire()
에서 대기 상태로 들어가요.
- 버퍼가 꽉 차면
- 소비자(consume)는 버퍼에서 데이터를 꺼내요.
- 버퍼가 비면
full.acquire()
에서 대기 상태로 들어가요.
- 버퍼가 비면
mutex
는 동시에 하나의 스레드만 버퍼를 수정할 수 있도록 보장해요.- 세마포어로 버퍼의 상태(
empty
와full
)를 관리해 생산과 소비의 균형을 맞춰요.
주요 동작 시각화 🚦
- 초기 상태:
buffer = []
,empty = 5
,full = 0
- 생산자가 데이터를 생성하고, 소비자는 대기.
- 생산자 활동:
- 생산자가 데이터를 추가하면
empty
감소,full
증가.
- 생산자가 데이터를 추가하면
- 소비자 활동:
- 소비자가 데이터를 소비하면
full
감소,empty
증가.
- 소비자가 데이터를 소비하면
학습 포인트 ✨
- 이 문제는 동기화, 세마포어, 뮤텍스의 조화를 이해하는 데 아주 유용해요.
- 생산자와 소비자의 속도를 조절하면서 버퍼의 효율적인 사용을 학습할 수 있어요.
2024.12.16 - [운영체제] - 프로세스 동기화란? : [해결방법][예시코드]
2024.12.16 - [운영체제] - [세마포어]심화 예제 : 예시 코드
반응형
SMALL
'운영체제' 카테고리의 다른 글
[운영체제] 페이지 교체 알고리즘 비교: FIFO, LRU, LFU 차이점과 동작 원리 (3) | 2024.12.18 |
---|---|
[운영체제] 컨텍스트 스위칭(Context Switching) 완벽 이해: 정의, 동작 원리와 비용 (1) | 2024.12.18 |
[운영체제] 프로세스 간 통신(IPC) 완벽 가이드: 파이프(Pipe) 개념과 Java·Python 구현 (1) | 2024.12.18 |
[운영체제] 프로세스 간 통신(IPC) 완벽 가이드: 메시지 큐 동작 원리와 Java·Python 예제 (0) | 2024.12.18 |
[운영체제] 프로세스 간 메모리 격리 완벽 이해: Java·Python 예제 코드와 원리 설명 (0) | 2024.12.18 |