운영체제

[운영체제] 세마포어(Semaphore)와 뮤텍스(Mutex)

테런 2023. 7. 30. 22:10
  • 세마포어(Semaphore)란?
세마포어(Semaphore)는 컴퓨터 과학과 운영 체제에서 동기화 기술을 구현하는 데 사용되는 개념입니다. 프로세스 또는 스레드가 공유 자원에 접근하는 것을 조절하고 조율하는 데 도움이 됩니다. 세마포어는 상호 배제(Mutual Exclusion)와 프로세스 간 통신(Interprocess Communication)의 문제를 해결하기 위해 개발되었습니다.

세마포어는 정수 변수로 표현되며, 다음과 같은 두 가지 기본 연산을 지원합니다.

1. P(Proberen) 연산: 세마포어 값을 1 감소시킵니다. 만약 세마포어의 값이 0이라면, 해당 프로세스 또는 스레드는 대기 상태로 들어가게 됩니다. 이때 다른 프로세스나 스레드가 V 연산을 수행하여 세마포어 값을 1 증가시키면, 대기 중인 프로세스나 스레드 중 하나가 다시 실행되게 됩니다.

2. V(Verhogen) 연산: 세마포어 값을 1 증가시킵니다. 만약 세마포어의 값이 0 이상이라면, 대기 중인 다른 프로세스나 스레드 중 하나를 실행 상태로 전환시킵니다.

세마포어는 주로 병렬 프로그래밍 환경에서 공유 자원에 대한 접근을 제어하는 데 사용됩니다. 예를 들어, 여러 스레드가 동시에 공유 변수에 접근하려는 경우, 세마포어를 사용하여 한 번에 하나의 스레드만 변수에 접근하도록 제한할 수 있습니다. 이렇게 함으로써 데이터의 일관성을 유지하고 경쟁 조건과 같은 문제를 방지할 수 있습니다.

세마포어는 다양한 종류가 있으며, 가장 기본적인 형태로는 이진 세마포어(Binary Semaphore)와 카운팅 세마포어(Counting Semaphore)가 있습니다. 이진 세마포어는 0 또는 1의 값만 가지며, 주로 상호 배제 문제를 해결하는 데 사용됩니다. 반면에 카운팅 세마포어는 양의 정수 값을 가지며, 자원의 개수를 나타내는 데 사용됩니다.

세마포어는 신중하게 사용해야 하며, 오용하거나 잘못된 사용은 교착 상태(Deadlock)와 같은 심각한 문제를 초래할 수 있습니다. 따라서 세마포어를 사용할 때에는 정확한 동기화 메커니즘을 설계하고, 신중하게 구현하는 것이 중요합니다.

 

  • Java 구현 예시 - 세마포어(Semaphore)
Java에서 세마포어를 구현하기 위해서는 java.util.concurrent.Semaphore 클래스를 사용할 수 있습니다. 이 클래스는 다중 스레드 환경에서 동기화를 위해 세마포어를 제공합니다. 이진 세마포어와 카운팅 세마포어 모두 구현 가능하며, 여기서는 이진 세마포어 예시입니다.

아래는 Java에서 이진 세마포어를 활용한 간단한 예시입니다:
import java.util.concurrent.Semaphore;

class SemaphoreExample {
    private static Semaphore binarySemaphore = new Semaphore(1); // 이진 세마포어 생성 (초기값: 1)

    static class Worker extends Thread {
        private String name;

        public Worker(String name) {
            this.name = name;
        }

        public void run() {
            try {
                binarySemaphore.acquire(); // 세마포어 점유 시도
                System.out.println(name + " is inside the critical section.");
                Thread.sleep(2000); // 임계 영역에서의 작업 시뮬레이션
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                binarySemaphore.release(); // 세마포어 반환
            }
        }
    }

    public static void main(String[] args) {
        // 여러 스레드 생성
        Thread thread1 = new Worker("Worker 1");
        Thread thread2 = new Worker("Worker 2");
        Thread thread3 = new Worker("Worker 3");

        // 스레드 시작
        thread1.start();
        thread2.start();
        thread3.start();

        // 모든 스레드가 종료될 때까지 대기
        try {
            thread1.join();
            thread2.join();
            thread3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All workers are done.");
    }
}​

위의 예시에서는 java.util.concurrent.Semaphore 클래스를 사용하여 이진 세마포어를 생성하고, 각 스레드가 세마포어를 획득(acquire())하고 반환(release())하도록 합니다. 이진 세마포어의 초기값을 1로 설정하여, 한 번에 하나의 스레드만 임계 영역에 접근할 수 있도록 보장합니다.

Worker 클래스는 스레드를 정의하며, run() 메서드에서 세마포어를 획득하고 임계 영역에서 작업을 수행한 후 세마포어를 반환합니다. Worker 객체를 여러 개 생성하여 여러 스레드가 동시에 실행되도록 합니다.

main 메서드에서는 세 개의 스레드를 생성하고 시작한 다음, 모든 스레드가 종료될 때까지 대기하고 종료 메시지를 출력합니다.

실행 결과로 세 개의 스레드가 순차적으로 임계 영역에 접근하여 작업을 수행함을 확인할 수 있습니다. 이를 통해 Java에서 세마포어를 사용하여 동기화를 어떻게 달성하는지 알 수 있습니다.

 

  • 뮤텍스(Mutex)란?
Mutex(뮤텍스)는 컴퓨터 과학과 운영 체제에서 동기화 기술을 구현하는 데 사용되는 개념입니다. Mutex는 상호 배제(Mutual Exclusion)를 제공하여 여러 프로세스 또는 스레드가 동시에 공유 자원에 접근하지 못하도록 하는 동기화 기법입니다.

상호 배제란, 한 번에 하나의 프로세스 또는 스레드만 공유 자원에 접근할 수 있도록 하는 것을 의미합니다. 이를 통해 여러 개의 프로세스 또는 스레드가 동시에 공유 자원에 접근하는 것을 방지하며, 데이터의 일관성과 무결성을 보장할 수 있습니다.

Mutex는 보통 두 가지 기본 연산을 지원합니다.

1. Lock (락) 연산: Mutex를 획득하여 공유 자원에 대한 접근 권한을 얻습니다. 다른 프로세스 또는 스레드가 이미 Mutex를 보유하고 있다면, Lock 연산은 대기 상태로 들어가고 해당 Mutex를 보유하고 있는 프로세스 또는 스레드가 Mutex를 해제할 때까지 기다립니다.

2. Unlock (언락) 연산: Mutex를 해제하여 다른 프로세스 또는 스레드가 공유 자원에 접근할 수 있도록 합니다.

Mutex는 주로 공유 자원에 대한 접근을 동기화하기 위해 사용됩니다. 예를 들어, 여러 스레드가 동시에 공유 변수에 접근하려는 경우 Mutex를 사용하여 한 번에 하나의 스레드만 변수에 접근하도록 제한할 수 있습니다. 이렇게 함으로써 데이터의 일관성을 유지하고 경쟁 조건과 같은 문제를 방지할 수 있습니다.

 

  • Java 구현 예시 - 뮤텍스(Mutex)
Java에서 Mutex를 구현하기 위해 java.util.concurrent.locks.Lock 인터페이스를 사용할 수 있습니다. 이 인터페이스는 상호 배제 기능을 제공하는 java.util.concurrent.locks.ReentrantLock 클래스의 구현을 사용하여 Mutex를 구현할 수 있습니다.

아래는 Java에서 Mutex를 활용한 예시입니다:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class MutexExample {
    private static Lock mutex = new ReentrantLock(); // Mutex로 사용할 ReentrantLock 객체 생성

    static class Worker extends Thread {
        private String name;

        public Worker(String name) {
            this.name = name;
        }

        public void run() {
            mutex.lock(); // Mutex 획득
            try {
                System.out.println(name + " is inside the critical section.");
                Thread.sleep(2000); // 임계 영역에서의 작업 시뮬레이션
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                mutex.unlock(); // Mutex 반환
            }
        }
    }

    public static void main(String[] args) {
        // 여러 스레드 생성
        Thread thread1 = new Worker("Worker 1");
        Thread thread2 = new Worker("Worker 2");
        Thread thread3 = new Worker("Worker 3");

        // 스레드 시작
        thread1.start();
        thread2.start();
        thread3.start();

        // 모든 스레드가 종료될 때까지 대기
        try {
            thread1.join();
            thread2.join();
            thread3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All workers are done.");
    }
}​

위의 예시에서는 java.util.concurrent.locks.ReentrantLock 클래스를 사용하여 Mutex를 구현합니다. ReentrantLock은 상호 배제를 제공하는 Lock의 구현 중 하나로, 여러 스레드가 동시에 임계 영역에 접근하지 못하도록 보호하는 데 사용됩니다.

Worker 클래스는 스레드를 정의하며, run() 메서드에서 ReentrantLock을 획득(lock())하고 임계 영역에서 작업을 수행한 후 반환(unlock())합니다. Worker 객체를 여러 개 생성하여 여러 스레드가 동시에 실행되도록 합니다.

main 메서드에서는 세 개의 스레드를 생성하고 시작한 다음, 모든 스레드가 종료될 때까지 대기하고 종료 메시지를 출력합니다.

실행 결과로 세 개의 스레드가 순차적으로 임계 영역에 접근하여 작업을 수행함을 확인할 수 있습니다. 이를 통해 Java에서 Mutex를 사용하여 동기화를 어떻게 달성하는지 알 수 있습니다.

 

  • 세마포어(Semaphore)와 뮤텍스(Mutex) 차이
세마포어(Semaphore)와 뮤텍스(Mutex)는 모두 동기화 기술로서, 다중 프로세스 또는 스레드 환경에서 공유 자원에 대한 접근을 제어하는데 사용됩니다. 하지만 세마포어(Semaphore)와 뮤텍스(Mutex)는 몇 가지 중요한 차이점이 있습니다.

1. 용도
- Mutex(뮤텍스)는 상호 배제(Mutual Exclusion)를 제공하기 위해 사용됩니다. 상호 배제란, 한 번에 하나의 프로세스 또는 스레드만 공유 자원에 접근할 수 있도록 하는 것을 의미합니다. 따라서 Mutex는 주로 공유 자원에 대한 임계 영역(critical section)을 보호하기 위해 사용됩니다.

- Semaphore(세마포어)는 Mutex의 확장된 개념으로, 상호 배제 뿐만 아니라 동시에 여러 개의 프로세스 또는 스레드가 공유 자원에 접근하는 것을 제어하기 위해 사용됩니다. Semaphore는 가용한 자원의 개수를 나타내는 카운팅 세마포어(Counting Semaphore)와 이진 세마포어(Binary Semaphore) 두 가지 형태로 사용될 수 있습니다.

2. 값의 범위
- Mutex(뮤텍스)는 이진 형태로 사용되며, 값이 0 또는 1인 상태를 가집니다. 즉, 한 번에 하나의 스레드만 Mutex를 소유할 수 있습니다.

- Semaphore(세마포어)는 양의 정수 값을 가집니다. 카운팅 세마포어는 가용한 자원의 개수를 나타내며, 값이 0 이상이면 해당 자원에 대한 접근이 가능합니다.

3. 사용 방법
- Mutex(뮤텍스)는 주로 상호 배제 문제를 해결하기 위해 사용됩니다. 임계 영역에 접근하기 전에 Mutex를 획득하여 임계 영역을 보호하고, 임계 영역을 빠져나올 때 Mutex를 해제합니다.

- Semaphore(세마포어)는 주로 자원의 수를 제한하거나 동시 접근을 허용하기 위해 사용됩니다. 카운팅 세마포어의 값이 0 이상인 경우에만 자원에 접근할 수 있으며, 자원 사용이 끝나면 Semaphore를 반환합니다.

4. 동기화 단위
- Mutex(뮤텍스)는 주로 개별적인 임계 영역에 대한 동기화에 사용됩니다. 각각의 Mutex는 하나의 임계 영역을 보호합니다.

- Semaphore(세마포어)는 여러 자원 또는 서비스의 동시 접근을 관리하는데 사용될 수 있습니다. 여러 개의 Semaphore를 사용하여 서로 다른 자원에 대한 동기화를 구현할 수 있습니다.

요약하면, Mutex는 단순한 상호 배제를 위해 사용되며 이진 형태로 동작합니다. 반면에 Semaphore는 Mutex의 확장된 형태로, 여러 자원의 접근을 제어하고 동시성을 관리하는 데 활용될 수 있습니다.