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.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);
댓글남기기