Semaphore의 가장 큰 문제는 결국 프로그래머의 실수로 귀결된다.
결론: 프로그래머에 synchronize를 모두 맡기기엔 불안하니, Language 자체에서 이를 보완해주는 기법을 만들겠다.
기본적으로 entry queue를 이용해서 monitor에 접근하고자 하는 task들의 출입을 제한한다.
각 CV에 의해서 wait중인 task는 monitor 밖이 아니라 내부의 queue를 이용해서 관리한다.
그럼에도 불구하고 monitor 내부에서 procedure를 실질적으로 실행시키는 tasks는 한개로 유지함을 통해서 mutual exclusion을 보장한다.
Monitor bounded_buffer {
buffer resources[N]; // shared_data
condition not_full, not_empty; // CV - we set this CV as semaphore in prev
procedure add_entry(resource x) {
while (array “resources” is full)
wait(not_full);
add “x” to array “resources”;
signal(not_empty);
}
procedure remove_entry(resource *x) {
while (array “resources” is empty)
wait(not_empty);
*x = get resources from array “resources”
signal(not_full);
}
}
-signal()
이 waiting thread를 깨우고 그 즉시 해당 thread로 context switch된다.
if (notReady) // 한번만 체크해도 상관없다. wait가 끝났을 때
wait(c);
signal()
은 waiting thread의 상태를 ready queue에 넣어줄 뿐, 진행 자체는 해당 thread에서 계속된다.broadcast()
연산이 가능하다.while (notReady) // wait가 끝나면 다시 한번 검사를 해야한다.(중간에 무슨일이 일어날지 모르므로)
wait(c);
Semaphore mutex = 1;
Semaphore next = 0;
int next_count = 0;
struct condition {
Semaphore sem;
int count;
} x = {0, 0};
procedure F() {
wait(mutex);
…
Body of F
…
if (next_count)
signal(next); // 다른 사람을 통해서 들어왔다면, 그 사람에게 넘긴다.
else
signal(mutex); // 없으면 lock을 풀어버려욧
}
procedure cond_wait(x) {
x.count++; // 대기 등록
if (next_count)
signal(next); // signal로 남에게 넘겨준 thread가 있으면 그 thread를 실행
else
signal(mutex); // 없으면 mutex 대기를 실행
wait(x.sem); // 이 thread는 잠들어야하고...
x.count--;
}
procedure cond_signal(x) {
if (x.count) { // waiting thread가 있음
next_count++; // 이 thread가 누군가를 깨웠음을 알림
signal(x.sem); // 대기중인 thread를 꺠운다.
wait(next); // 다음 thread에 넘기기 위해서 지금 실행중인 thread는 waiting으로 돌린다.
next_count--; // waiting에서 나오면 count를 한개 줄인다.
}
}
Condition variables do not have any history, but semaphores do
On a condition variable signal(), if no one is waiting, the signal is a no-op
On a semaphore signal(), if no one is waiting, the value of the semaphore is increased