[Philosophers] 예시/예제로 보는 뮤텍스와 세마포어의 차이

이대현·2021년 3월 21일
3

42SEOUL

목록 보기
27/27

뮤텍스(Mutex)와 세파모어(Semaphore)는 모두 공유자원의 상호배제(Mutual Exclusion)를 달성하기 위해 고안된 기법이다. 즉, 동시성 프로그래밍의 가장 큰 숙제인, "여러 프로세스나 스레드가 공유자원에 접근하는 것을 제어(관리)하기 위한 방법"이며 아래와 같이 서로 다른 방식을 사용한다.

  • 뮤텍스(Mutex) : 한 프로세스(스레드)가 소유하는 어떤 오브젝트 를 기반으로 한 상호배제기법
  • 세마포어(Semaphore) : 현재 공유자원에 접근할 개수 있는 프로세스(스레드)의 개수를 나타내는 카운팅 변수값을 두는 방식의 상호배제기법

1. 예시로 보는 뮤텍스와 세마포어

1.1 뮤텍스

뮤텍스는 화장실이 하나 밖에 없는 식당과 비슷하다.
화장실을 가기 위해서는 카운터에서 키를 받아가야 한다. 카운터에 키가 있다면 화장실에 사람이 없다는 뜻이고 그 키를 이용해 화장실에 들어갈 수 있다.
다른 손님들은 화장실을 이용 중인 손님이 용무를 마치고 키를 카운터에 반납할 때 까지 화장실에 들어갈 수 없으며 대기해야한다.

이것이 뮤텍스가 동작하는 방식이다.

  • 화장실 == 공유자원
  • 화장실을 이용하는 손님들 == 프로세스(스레드)
  • 대기 줄 == 큐
  • 화장실 키 == 공유자원에 접근하기 위해 필요한 어떤 오브젝트(뮤텍스)

1.2 세마포어

세마포어는 화장실이 여러 개 있고, 화장실 입구에는 현재 빈 화장실의 개수를 알려주는 전광판이 있는 식당과 비슷하다.
모든 칸의 화장실이 사용중일 때 전광판의 숫자는 0이 되며, 손님들은 전광판 숫자가 1로 바뀔 때까지 대기해야한다.
용무를 마친 손님은 화장실에서 나오면서 전광판의 숫자를 +1 해주고, 대기하던 손님은 이 숫자에서 -1 해 준 뒤에 화장실을 이용할 개수 있다.

즉, 세마포어는 공통으로 관리하는 하나의 값을 이용해 상호배제를 달성한다.

  • 화장실 == 공유 자원
  • 화장실을 이용하는 손님들 == 프로세스(스레드)
  • 대기 줄 == 큐
  • 전광판의 숫자 == 공유자원에 접근가능한 프로세스의 개수를 나타내는 어떤 변수(세마포어)

2. 예제로 보는 뮤텍스와 세마포어

2.1. 뮤텍스

뮤텍스는 초기값을 1과 0으로 가진 변수이다. 1이면 카운터에 화장실 키가 있다는 뜻이다.

임계영역에 들어갈 때 락(lock)을 걸어 다른 프로세스(스레드)가 접근하지 못하도록 하고, 임계영역에서 나와 해당 락을 해제(unlock) 한다.

뮤텍스를 활용해 상호배제를 아래 코드처럼 구현할 수 있다.

mutex = 1;

void lock () {
	while (mutex != 1) 
  {
    // mutex 값이 1이 될 때까지 대기
  }
    // 이 구역에 도착했다는 것은 화장실 키를 획득 했다는 것이고 mutex 값이 1이라는 뜻. 이제 뮤텍스 값을 0으로 만들어 다른 프로세스(혹은 쓰레드)의 접근을 제한.
   mutex = 0;
}

void unlock() {
	// 임계 구역(화장실)에서 나온 프로세스는 다른 프로세스가 접근할 개수 있도록 뮤텍스 값을 1으로 만들어 락을 해제.
	mutex = 1;
}

2.2. 세마포어

세마포어는 정개수 값을 가지는 변수이다. 그 수는 공유자원에 접근가능한 프로세스의 개수를 의미한다. 세마포어를 활용하기 위해 아래와 같은 구조를 만들어준다.

struct semaphore {
	int count;
    	queueType queue;
};

void semWait (semaphore s) {
	s.count--;
    if (s.count < 0) 
    {
    	// 이 구역으로 들어왔다는 것은 현재 프로세스(스레드)가 공유 자원에 접근할 수 없다는 것을 의미.
    	// 요청한 프로세스를 s.queue에 연결
      // 요청한 프로세스를 블록 상태로 락
    }
} 

void semSignal (semaphore s) {
	s.count++;
    if (s.count <= 0) 
    {
    	// 대기하고 있는 프로세스(스레드)를 위해 s.queue에 연결되어 있는 프로세스를 큐에서 제거
      // 프로세스의 상태를 실행 가능으로 변경하고 ready list에 연결
    }
}
  • semWait() 연산: 세마포어 값을 감소시킨다. 만일 값이 음수가 되면 semWait를 호출한 프로세스는 블록(락) 된다. 즉, 음수가 아니면, 프로세스는 계속 수행될 수 있다.
  • semSignal() 연산: 세마포어 값을 증가시킨다. 만약 값이 음수이면, semWait 연산에 의해 블록된 프로세스들을 해제한다.

아래는 세마포어를 활용한 상호배제의 간단한 예시 코드이다.

const int n = // 프로세스 개수
semaphore s = 1;

void Process (int i) {
	while (true) 
  {
     semWait(s); //세마포어 값을 감소시킨다.
        
     /* 임계 영역(Critical Section) */
        
     semSignal(s); //세마포어 값을 증가시킨다.
        
     /* 임계 영역 이후 코드 */
   }
}

void main() {
	parbegin (Process(1), Process(2), ..., Process(n));
}

3. 정리

  • Mutex는 상태가 0, 1 두 개 뿐인 binary Semaphore 라고 볼 수 있다.

  • Semaphore는 프로세스(스레드)가 소유할 수 없는 반면, Mutex는 소유가 가능하며 프로세스(스레드)가 이에 대한 책임을 진다. (Mutex 의 경우 상태가 두개 뿐인 lock 이므로 lock 을 ‘가질’ 수 있다.)

  • Mutex는 동기화 대상이 오직 하나뿐일 때, Semaphore는 동기화 대상이 하나 이상일 때 사용한다.

  • Semaphore는 시스템 범위에 걸쳐있고 파일 시스템 상의 파일 형태로 존재한다. 반면 Mutex는 프로세스 범위를 가지며 프로세스가 종료될 때 자동으로 해제된다.

4. 참고

profile
삽질의 기록들 👨‍💻

0개의 댓글