[OS] 세마포어와 뮤텍스

hyng·2022년 3월 30일
0

os

목록 보기
3/5

세마포어와 뮤텍스

  • 멀티 스레딩 환경에서는 전역변수, 힙 영역 등 스레드들끼리 공유되는 자원이 존재합니다.
  • 이런 공유 자원들에 접근하는 코드 영역을임계 영역이라고 합니다.
  • 임계 영역은 상호배제(각 스레드는 공유 자원의 손상을 방지하기 위해 배타적으로(독점적으로) 공유 자원을 사용해야 함) 되어야 합니다.
  • 그렇지 않으면 race condition문제가 발생할 수 있습니다.
  • 임계 영역을 상호배제하는 방법이 바로 세마포어와 뮤텍스입니다.

세마포어

  • 세마포어는 동시에 임계 영역에 접근 가능한 스레드 수를 정해, 그 수만큼 스레드가 동시에 임계 영역에 접근할 수 있도록 합니다.

  • 각 스레드들은 임계영역에 접근할때 카운트 값을 1 감소시키고 임계영역에서 빠져나올때 카운트값을 1 증가시킵니다.

  • 세마포어를 java 코드로 작성해보면 아래와 같습니다.

public class Semaphore{
    int count; //동시에 임계영역에 접근할 수 있는 스레드 수
    public Semaphore(int count){
        this.count = count;
    }
    public synchronized void Wait(int index){
        //lock
        count--;
        if(count < 0) //이 객체의 대기큐에서 기다림
            System.out.println(index + " 스레드 현재 큐에서 대기중");
            this.wait();
    } //unlock
    
    public synchronized void Signal(int index){
        //lock
        count++;
        if(count <= 0) // 기다리는 스레드가 있음
            this.notify(); // 대기큐에서 대기중인 스레드 중 하나를 깨움
            System.out.println(index + " 스레드가 대기큐에서 스레드 깨움");

    
    }  //unlock
  • 참고로 java에서는 모든 객체가 대기 큐를 가지고 있으며, 각 객체에 대해 wait(), notify(), notifyAll() 멤버 함수를 사용할 수 있습니다.

  • wait()를 호출하여 대기 상태가 되면 synchronized에 의해 획득한 lock은 자동으로 풀리고, 나중에 nofity() 의해 깨어나면 자동으로 다시 lock을 획득합니다.

  • 위의 코드를 테스트 해보기 위해 main클래스를 작성합니다.

public class Main{
    public static void main(String[] args) throws Exception{
        Semaphore semaphore = new Semaphore(1);
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                semaphore.Wait(1);
                Thread.sleep(1000);
                semaphore.Signal(1);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
                semaphore.Wait(2);
                Thread.sleep(1000);
                semaphore.Signal(2);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

    }
}

  • 테스트를 위해서 sleep()을 중간 중간 호출합니다.

  • 어떤 스레드가 먼저 실행되냐에 따라 대기 큐에 들어가는 스레드가 바뀔 수 있습니다.

  1. 첫 번째 스레드가 Wait()에서 count 수를 하나 감소시키고 리턴한 다음 sleep()로 인해 멈춰있는 동안 두 번째 스레드가 Wait()에 진입합니다.
  2. 아직 첫 번째 스레드에서 Signal() 메서드를 실행하지 못했기 때문에 count 값은 0인 상태로 존재합니다.
  3. 그래서 두 번째 스레드는 우리가 만든 메서드 Wait()에서 wait()을 호출하고 대기 큐에 들어가 대기하게 됩니다.
  4. 첫 번째 스레드가 sleep()에서 깨어나서 Signal()을 호출하고 notify()를 통해 대기 큐에서 대기 중인 두 번째 스레드를 깨웁니다.
  5. 그래서 실행 결과 콘솔 결과는 아래와 같습니다.

뮤텍스

  • 오직 하나의 스레드만이 임계 영역을 lock하여 사용하고, 임계 영역에 접근하려는 다른 스레드는 임계 영역이 unlock 될 때까지 접근할 수 없습니다.

세마포어와 뮤텍스의 차이점

  • 세마포어는 정해진 수만큼의 스레드가 동시에 임계 영역에 접근할 수 있습니다.
  • 세마포어의 카운트를 1로 설정하면 뮤텍스처럼 사용할 수 있습니다.
  • 뮤텍스는 오직 하나의 스레드만이 임계 영역에 접근할 수 있습니다.

참고

https://junghyun100.github.io/Semaphore&Mutex/
https://mangkyu.tistory.com/104
https://velog.io/@logandev/%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4%EC%99%80-%EB%AE%A4%ED%85%8D%EC%8A%A4-%EC%B0%A8%EC%9D%B4
https://worthpreading.tistory.com/90

profile
공부하고 알게 된 내용을 기록하는 블로그

0개의 댓글