운영체제

[운영체제] 생산자-소비자 문제 완벽 이해: 세마포어와 뮤텍스를 활용한 동기화 예제 코드

CodeCaine Explorer 2024. 12. 18. 09:48
728x90
반응형
SMALL

생산자-소비자 문제 🧩

생산자-소비자 문제공유 자원(Buffer)를 통해 데이터를 주고받는 대표적인 동기화 문제예요.

  • 생산자: 데이터를 생성해서 버퍼에 추가하는 역할
  • 소비자: 버퍼에서 데이터를 꺼내 사용하는 역할

문제는 생산자와 소비자가 동시에 버퍼에 접근하면 충돌이 발생할 수 있다는 점이에요.
이를 해결하기 위해 뮤텍스(Mutex)세마포어(Semaphore)를 사용해요.


동작 원리 🛠️

  1. 버퍼(Buffer): 데이터를 저장하는 공간. 버퍼가 꽉 차면 생산자는 대기하고, 버퍼가 비면 소비자는 대기해야 해요.
  2. 세마포어 사용
    • empty: 버퍼의 빈 공간 개수를 관리.
    • full: 버퍼의 채워진 데이터 개수를 관리.
  3. 뮤텍스 사용: 버퍼에 접근할 때 한 번에 하나의 스레드만 접근하도록 보장.

자바 예제 ☕

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()

동작 설명 🎯

  1. 생산자(produce)는 데이터를 생성해서 버퍼에 추가해요.
    • 버퍼가 꽉 차면 empty.acquire()에서 대기 상태로 들어가요.
  2. 소비자(consume)는 버퍼에서 데이터를 꺼내요.
    • 버퍼가 비면 full.acquire()에서 대기 상태로 들어가요.
  3. mutex동시에 하나의 스레드만 버퍼를 수정할 수 있도록 보장해요.
  4. 세마포어로 버퍼의 상태(emptyfull)를 관리해 생산과 소비의 균형을 맞춰요.

주요 동작 시각화 🚦

  1. 초기 상태:
    • buffer = [], empty = 5, full = 0
    • 생산자가 데이터를 생성하고, 소비자는 대기.
  2. 생산자 활동:
    • 생산자가 데이터를 추가하면 empty 감소, full 증가.
  3. 소비자 활동:
    • 소비자가 데이터를 소비하면 full 감소, empty 증가.

학습 포인트 ✨

  • 이 문제는 동기화, 세마포어, 뮤텍스의 조화를 이해하는 데 아주 유용해요.
  • 생산자와 소비자의 속도를 조절하면서 버퍼의 효율적인 사용을 학습할 수 있어요.

2024.12.16 - [운영체제] - 프로세스 동기화란? : [해결방법][예시코드]

 

프로세스 동기화란? : [해결방법][예시코드]

프로세스 동기화란? 🧩"프로세스 동기화"는 여러 프로세스(또는 스레드)가 동시에 같은 공유 자원에 접근할 때, 문제가 발생하지 않도록 제어하는 기술이에요.문제가 생기는 이유는 경쟁 상태(R

alswnsghd1234.tistory.com

2024.12.16 - [운영체제] - [세마포어]심화 예제 : 예시 코드

 

[세마포어]심화 예제 : 예시 코드

심화된 예제 🧠이번에는 세마포어(Semaphore)를 사용해 여러 스레드가 제한된 수의 자원을 동시에 사용하는 심화 예제를 다뤄볼게요.예를 들어, 3대의 차량만 동시에 주차할 수 있는 주차장을 구

alswnsghd1234.tistory.com

 

반응형
SMALL