06 모니터

  • 고수준의 병행성 제어구조
  • 5장의 프로세스 동기화는 저수준의 병행성 제어 구조임

세머퍼를 이용한 문제

  • 프로세스 동기화를 위해 사용
  • 잘못 사용하면 발견하기 어려운 타이밍 오류발생 (특정 순서로 진행하는 경우만 발생하고 항상 이러한 순서로만 실행되는 것은 아님)
  • 세머퍼 타이밍 오류의 예
    • mutex: 1로 초기화되는 세머퍼
    • signal(mutex) …. wait(mutex)
      • 상호 배제 요구조건을 위반하는 결과를 유발한다
    • wait(mutex) … wait(mutex)
      • 데드락을 유발한다
    • wait(mutex)나 signal(mutex) (또는 둘 다)를 생략하기
      • 상호 배제가 위반되거나 데드락이 발생할 수 있다
  • 이런 에러를 처리하기 위해서, 모니터\(_{monitor}\)가 사용될 수 있다

모니터\(_{Monitor}\)

  • 프로세스 동기화를 위한 편리하고 효율적인 방법론을 제공하는 높은 수준의 추상화
  • 상호배제 및 프로세스 동기화를 위한 소프트웨어 모듈
    • 프로그래밍 언어 수준에서 제공
    • 세마포어와 비슷한 역할을 수행하나 사용이 훨씬 쉬움
  • 데이터와 프로시저를 모두 포함하는 객체
  • Brinch Hansen와 Tony Hoare가 만듦
  • 정의
    • 여러 프로세스들간에 공유되는 공유 데이터 + 임계지역 코드(공유데이터에 접근하는 프로시저)들의 집합
    • 공유 데이터와 임계지역 코드를 포함하는 울타리의 개념
    • 병행 프로그래밍 지원
  • 특징
    • 지역 변수는 모니터 내부에서만 접근 가능
    • 프로세스는 모니터 프로시저 중 하나를 호출함으로써 모니터 내부로 진입
    • 한 시점에 단 하나의 프로세스만 모니터 내부에서 수행 가능

모니터의 구조

  • 모니터 진입 큐\(_{Monitor \ Entry \ Queue}\)
    • 모니터에 진입하려는 프로세스 또는 스레드들이 대기하는 큐
  • 공유 데이터와 프로시저 간 상호 관계
    • 모니터에는 공유 데이터와 프로시저가 함께 정의되어 있다.
  • 조건 큐\(_{Condition \ Queue}\)
    • 특정 조건이 만족되지 않아서 진행할 수 없는 프로세스나 스레드들을 대기시키는 큐
    • 조건 변수와 연관
  • 신호 제공자 큐\(_{Signal \ Queue}\)
    • 조건이 만족되었을 때, 대기 중인 프로세스나 스레드를 깨워 실행할 수 있도록 신호를 보내는 큐

모니터와 진입 큐

  • 구성 요소
    • 공유 데이터
    • 연산들
    • 초기화 코드
  • 공유 데이터는 연산들에 의해서 접근 가능
  • 한 순간에 오직 하나의 프로세스만이 모니터에 서 활성화된다
    • 프로그래머는 이 동기화를 위해 동기화 제약 조건을 명시적으로 코딩 할 필요가 없다

조건 변수들\(_{condition \ Variables}\)

  • 기본 모니터는 어떤 동기화 기법을 모델링하는 데에는 충분하지 않음
  • 위 문제를 해결하기 위해 조건 변수를 사용
    • 조건\(_{condition}\) x, y;
    • 프로그래머는 하나 이상의 조건 유형의 변수를 정의 가능
  • 조건 변수에 적용되는 두 가지 연산
    • x.wait ()
      • 이 연산을 호출한 프로세스는 일시 중단 (대기)
    • x.signal ()
      • x.wait ()을 호출했던 프로세스들 중에 하나를 다시 시작 (일시 중단된 프로세스가 있는 경우)
      • 한 개의 유보되었던 프로세스만을 재개
      • 유보된 프로세스가 한 개도 없다면, 아무것도 일어나지 않는다
  • 주요 사항
    • 가정
      • x.signal()이 프로세스 P에 의해 호출되었을 때, 조건 변수 x와 관련하여 일시 중단된 프로세스 Q가 존재
    • 만약 일시 중단된 프로세스 Q가 실행을 재개하도록 허용된다면, 신호를 보낸 프로세스 P는 대기해야 함
      • 그렇지 않다면, 프로세스 Q와 P가 동시에 활성화될 것이다
    • 두 가지 가능성:
      • 신호와 대기: 프로세스 P는 프로세스 Q가 모니터를 떠날 때까지 기다리거나 다른 조건을 기다려야 한다
      • 신호와 계속: 프로세스 Q는 프로세스 P가 모니터를 떠날 때까지 기다리거나 다른 조건을 기다려야 한다

식사하는 철학자의 해결방안

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
monitor DP
{ 
	enum {THINKING; HUNGRY, EATING} state [5] ;
	condition self [5];

	void pickup (int i) { 
		state[i] = HUNGRY;
		test(i);
		if (state[i] != EATING) self [i].wait();
	}

	void putdown (int i) { 
		state[i] = THINKING;
		// 왼쪽과 오른쪽 이웃을 테스트한다
		test((i + 4) % 5);
		test((i + 1) % 5);
	}
  • 철학자들은 세가지 상태를 가짐
  • Ph_j는 배고파졌을때, 양쪽 젓가락 두 개를 모두 잡을 수 없다면 대기한다.
  • pickup함수는 젓가락을 드는 것
    • Ph_i는 먹기 전 pickup 연산 호출
  • putdown함수는 젓가락을 내려놓는 것
    • Ph_i는 먹고 난 후 putdown 연산 호출

test 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test (int i) 
{ 
	if ( (state[(i + 4) % 5] != EATING) &&
	(state[i] == HUNGRY) &&
	(state[(i + 1) % 5] != EATING) ) 
	{ 
		state[i] = EATING ;
		self[i].signal() ;
	}
}

initialization_code() { 
	for (int i = 0; i < 5; i++)
	state[i] = THINKING;
}
  • Ph_i는 젓가락을 집기 위해 test(i)연산을 수행
  • Ph_i는 젓가락을 놓기 위해 test(i + 1)와 test((i + 1) % 5)를 수행
  • 양쪽 이웃이 먹지 않을 때, 그녀는 상태를 먹기(EATING)로 바꾸고 자기 자신을 호출
  • 이후 initialization_code를 통해 생각하는 상태로 초기화

철학자 i가 pickup(), putdown() 연산 호출

1
2
3
4
5
6
7
do
{
	thinking
	dp.pickup( i )
	eat
	dp.putdown ( i );
} while (TRUE);

댓글남기기