Classical Problems of Synchronization

- Bounded-Buffer Problem (Producer-Consumer Problem)
- Readers and Writers Problem
- Dining-Philosophers Problem

생산자-소비자의 문제
- 주황색 원은 생산자가 공유 버퍼에 데이터를 넣어 놓은 상태, 흰색 원은 소비자가 버퍼에서 프로세스를 꺼내간 상태.
- 데이터를 넣을 때 or 꺼낼 때 lock을 걸어 공유 데이터에 다른 프로세스가 접근하지 못하도록 함.
- 버퍼가 다 찬 상태에서 버퍼에 데이터를 넣기 위해 생산자가 도착했다고 가정했을 때, 빈 버퍼가 없어 데이터를 넣을 수 없다는 문제가 생김. 소비자가 도착해 버퍼에 있는 데이터를 꺼내 가져갈 때까지 버퍼에 데이터를 삽입할 수 없음. 생산자가 무한 대기 해야하는 상황이 발생. 마찬가지로, 반대 상황 (꺼내갈 데이터가 없어 소비자가 무한 대기하는 현상) 도 발생할 수 있다는 문제점 존재.
do{
P(empty);
P(mutex);
...
add x to buffer
...
V(mutex);
V(full);
} while(1);
do{
produce and item in x
...
P(full);
P(mutex);
...
remove an item from buffer to y
...
V(mutex);
V(empty);
...
consume the item in y
...
} while(1);
Shared Data
DB 자체readcount; /현재 DB에 접근 중인 Reader의 수/Synchronization variables
mutex : 공유 변수 readcount를 접근하는 코드 (critical section)의 mutual exclusion 보장을 위해 사용.db : reader와 writer가 공유 DB 자체를 올바르게 접근하게 하는 역할Shared data
int readcount = 0;
DB 자체;
Synchronization variables
semaphore mutex = 1, db = 1;
P(db);
...
writing DB is performed
...
V(db);
! Starvation 발생 가능
P(mutex);
readcount++;
if(readcount == 1) P(db); // block writer
V(mutex); // readers follow
...
reading DB is performed
...
P(mutex);
readcount--;
if(readcount == 0) V(db); // enable writer
V(mutex);
Synchronization variables
semaphore chopstick[5]; // initially all values are 1
Philosopher i
do {
P(chopstick[i]);
P(chopstick[(i+1) % 5]);
...
eat();
...
V(chopstick[i]);
V(chopstick[(i+1) % 5]);
...
think();
...
} while(1);
생각하는 일 / 밥먹는 일 두개로 나누어짐
밥을 먹게 되면 왼쪽, 오른쪽 젓가락을 같이 집어 밥을 먹음. 밥을 먹을 때는 옆에 있는 젓가락을 잡아야 먹을 수 있음
젓가락을 공유 자원으로 빗댄 문제
deadlock 문제 발생 가능성 존재
본인이 밥을 먹고 배가 부른 상태여야 젓가락을 놓기 때문에 한 젓가락만 가지고 있는 상태라면 아무도 밥을 먹지 못하고 아무도 젓가락을 놓지 못한 채로 계속 대기할 가능성 존재
앞의 해결방안의 문제점
해결방안
enum {thinking, hungry, eating} state[5];
semaphore self[5] = 0;
semaphore mutex=1;
Philosopher i
do {
pickup(i);
eat();
putdown(i);
think();
} while(1);
void putdown(int i){
P(mutex);
state[i] = thinking;
test((i+4) % 5);
test((i+1) % 5);
V(mutex);
}
void pickup(int i){
P(mutex);
state[i] = hungry;
test(i);
V(mutex);
P(self[i]);
}
void test(int i){
if(state[(i+4) % 5] != eating && state[i] == hungry && state[(i+1) % 5] != eating) {
state[i] = eating;
V(self[i]);
}
}
Semaphore의 문제점
✔ 코딩하기 힘들다.
✔ 정확성(correctness)의 입증이 어렵다.
✔ 자발적 협력이 필요하다.
✔ 한번의 실수가 모든 시스템에 치명적인 영향을 끼친다.
예

monitor monitor-name
{
shared variable declarations
procedure body P1(..) {
...
}
procedure body P2(..) {
...
}
procedure body Pn(..) {
...
}
{
initializaion code
}
}

condition x,y;wait와 signal 연산에 의해서만 접근 가능x.wait();x.signal();Monitor Bounded Buffer Implementation
monitor bounded_buffer
{
int buffer[N];
condition full, empty;
/* condition var.은 값을 가지지 않고 자신의 큐에 프로세스를
매달아서 sleep 시키거나 큐에서 프로세스를 깨우는 역할만 함 */
void produce(int x)
{
if there is no empty buffer
empty.wait();
add x to an empty buffer
full.signal();
}
void consume(int *x)
{
if there is no full buffer
full.wait();
remove an item from buffer and store it to *x
empty.signal();
}
}
monitor dining_philosopher
{
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(); /*wait here*/
}
void putdown(int i) {
state[i] = thinking;
/* test left and right neighbors*/
test((i+4) % 5); /*if L is waiting*/
test((i+1) % 5);
}
void test(int i) {
if ((state[(i + 4) % 5] != eating) &&
(state[i] == hungry) &&
(state[(i + 1) % 5] != eating)) {
state[i] = eating;
self[i].signal(); /*wake up Pi*/
}
}
void init() {
for (int i = 0; i < 5; i++)
state[i] = thinking;
}
}
// Each Philosopher:
{
pickup(i);
eat();
putdown(i);
think();
} while(1)
Deadlock Example 1
Deadlock Example 2

1.Mutual Exclusion (상호 배제)
이 조건들을 충족하지 않으면 교착상태는 발생하지 않음.


deadlock이 아니다.자원 할당 시 Deadlock의 4가지 필요 조건 중 어느 하나가 만족되지 않도록 하는 것
=> Utilization 저하, throughput 감소, starvation 문제
- 자원 요청에 대한 부가적인 정보를 이용해서 deadlock의 가능성이 없는 경우에만 자원을 할당
- 자원 할당이 deadlock으로부터 안전(safe)한지를 동적으로 조사해서 안전한 경우에만 할당. (항상 safe 상태 유지)
시스템 state가 원래 state로 돌아올 수 있는 경우에만 자원 할당
가장 단순하고 일반적인 모델은 프로세스들이 필요로 하는 각 자원별 최대 사용량을 미리 선언하도록 하는 방법임.
Resource Allocation Graph Algorithm (자원 할당 그래프)
리소스 당 하나의 인스턴스일 때 회피 방법
Banker's Algorithm (은행가 알고리즘)
리소스 당 여러 개의 인스턴스일 때 회피 방법 
Deadlock 발생은 허용하되 그에 대한 detection 루틴을 두어 deadlock 발견 시 recover
Resource type 당 single instance일 경우
Resource type 당 multiple instance인 경우
Wait-for graph 알고리즘
Resource type 당 single instance인 경우
Wait-for graph
Algorithm
Recovery