Lecture 15. Readers-Writers-Priority
The Readers/Writers Problem
항공기 예약 시스템을 볼 때 여러 명의 사람들이 공유 데이터를 읽을 수도 있고 여러 사람이 공유 데이터를 쓸 수 있어야 합니다. 하지만 반드시 한 명의 Writer가 공유 데이터의 값을 변경하고 있을 때는 다른 어떤 사람도 해당 데이터에 접근하면 안됩니다.
- RRR(OK)
- RWW(NOT-OK)
- RWR(NOT-OK)
이를 해결하기 위해서는 R과 W의 우선순위를 정해야합니다.
- R이 W보다 우선 순위가 높다.
- W이 R보다 우선 순위가 높다.
- 대부분의 경우 우선순위가 없는 집단은 starvation을 피하기 어렵습니다.
Readers & Writers
//READERS
P(MUTEX);
readers++;
if(readers == 1){
P(write);
}
V(MUTEX);
read database
P(MUTEX);
readers--;
if(readers == 0){
V(write);
}
V(MUTEX);
//WRITERS
P(Write);
write database;
V(write);
Readers 설명
- reader 변수에 대해서 MUTEX한 접근이 보장되어야 합니다. 그래서 P(MUTEX)와 V(MUTEX)가 사용됩니다.
- 하지만 database를 읽고 있는 도중에는 MUTEX가 보장될 필요가 없습니다.
- 만약 reader가 맨 처음의 reader라면, P(Write)를 진행합니다. Semaphor P()는 현재 CS에 누가 들어가 있으면 기다리고, CS에 있는 Thread가 semaphor V()를 진행하면서 깨워주는 것을 기다리는 명령입니다. CS에 아무도 없다면 CS에 들어갑니다.
- 만약 다른 Thread가 read하려고 한다면 P(MUTEX);에서 기다리게 됩니다.
- 최초의 Thread가 data base를 읽고 있을 때, 다른 Thread가 따라온다면 database를 같이 읽을 수 있습니다.
- 마짐가 Reading Thread가 끝날 때, waiting writer가 있다면 이 중 하나를 반드시 깨워야 합니다.
writer 설명
- active writer가 있다면 뒤늦게 온 writer는 기다려야 합니다.
- active reader가 있다면 writer는 기다려야 합니다.
- write는 data base에 active reader와 active writer가 모두 없을 때 쓸 수 습니다.
- writer가 쓰기 작업을 끝낸다면, Reader나 writer 상관 없이 하나를 깨워야 합니다.
- 만약 Reader가 next가 된다면, P(write);에서 나와서 V(MUTEX);를 지나 read database를 합니다.
- read database를 하는 최초의 Thread가 V(MUTEX);를 나오면 다른 Thread들은 P(MUTEX) 와 V(MUTEX)를 바로 통과해서 다같이 read database를 할 수 있습니다.
Readers & Writers Priority
Using semaphor
- AW : Active Writer
- WW : Waiting Writer
- AR : Active Reader
- WR : Waiting Writer
//Readers
P(MUTEX);
if(AW + WW > 0){ //만약 기다리거나 활동중이 Writer가 있으면
WR++; //기다립니다.
}else{ //만약 기다리거나 활동중이 Writer가 없으면
V(OKToRead); //따라 들어온 Thread도 한 번에 읽을 수 있게 미리 -1해눕니다.
AR++; //읽습니다.
}
V(MUTEX); //최초의 Read가 V(MUTEX)를 진행하면 다른 Thread도 따라 들어옵니다.
P(OKToRead); //-1 한 결과가 음수가 아니므로 따라 들어온 Thread도 동시에 read database 합니다.
read database;
AR--; //읽기를 종료합니다.
if(AR== 0 && WW > 0){//마지막 쓰레드가 끝나고 기다리고 있는 writer가 있으면
V(OKToWrite); // 설명 1
WW--; // 기다리지 말고
AW++; // active 하라고 말합니다.
}
V(MUTEX);
//writers
P(MUTEX);
if(AW + AR > 0){ //만약 기다리거나 활동중인 Thread가 있으면
WW++; //기다립니다.
}else{ //활동 중인게 없으면
V(OKToWrite); // + 1
AW++; //활동을 시작합니다.
}
V(MUTEX); // AW++ 했기 때문에 P(MUTEX)에서 들어와도 if(AW+AR > 0)를 통과 못합니다.
P(OKToWrite); // - 1
Write database;
AW--;
if( WW > 0){ // writer의 우선순위가 read보다 높기 때문에 writer 먼저
V(OKToWrite);// 설명 1
AW++; // Active writer를 올립니다.
WW--; // 기다리고 있는 writer 줄입니다.
}else if(WR > 0){// 우선순위가 낮은 R은 WW가 없을 때 깨어납니다.
V(OKToRead); // 설명 2
AR++;
WR--;
}
V(MUTEX);
- 설명 1 : write가 여러번 줄 서서 들어오려고 할 수 있습니다. 이 때 미리 +1을 해두지 않으면 P를 실행할 때 OKToWrite < 0인 경우가 발생해서 불필요한 wait를 할 수 있습니다. 이를 막기 위해서 미리 + 1을 해두는 부분입니다.
- 설명 2 : 설명 1의 과정을 reader에게도 적용하는 부분입니다.
Using Lock & condition Variable Solution 1
//Readers
acquire(MUTEX); // Just for MUTEX, Use LOCK
while(AW + WW > 0){ // 우선순위가 더 높은 writer가 있을 때
WR++; // Reader는 일단 기다렸다가
wait(OKToRead); // 읽어도 되면
WR--; // wait 줄이고
}
AR++; // active로 전환
release(MUTEX); // Just for MUTEX, Use LOCK
read database; // writer가 하나도 없으면 여러 Thread 동시에 읽기 가능
acquire(mutex); // Just for MUTEX, Use LOCK
AR--; // 하나씩 끝내고
if(AR== 0 && WW > 0){ // 읽는 마지막 Thread 사라질 때 WW 있으면
signal(OKToWrite);// 깨우기
}
release(MUTEX); // Just for MUTEX, Use LOCK
//writers
acquire(MUTEX); // Just for MUTEX, Use LOCK
while(AW + AR > 0){ // Active가 하나라도 있으면
WW++; // 기다렸다가
wait(OKToWrite); // 써도 되면
WW--; // 기다리는거 줄이고
}
AW++; // Active 늘리기
release(MUTEX); // Just for MUTEX, Use LOCK
Write database; // WW를 올려도 AW+AR > 0은 변함이 없어서 하나만 Write 가능
acquire(MUTEX); // Just for MUTEX, Use LOCK
AW--; // AW를 줄이는 순간 다른 Writer 들어올 수 있음
if( WW > 0){ // 종료할 때 WW있으면
signal(OKToWrite);// 깨우고
}else { // WW 없으면
broadCast(OKToRead); // WR 모두 깨우기
}
Release(MUTEX); // Just for MUTEX, Use LOCK
Using Lock & condition Variable Solution 2
- i > 0 : i counts number of active readers
- i == 0: no one is accessing the data
- i < 0 : there is an active writer
이정도는 이제 바로 이해할 수 있을 것입니다.
``cpp acquire(MUTEX); while(i<0){ wait(access); } i++; release(MUTEX);
read database;
acquire(MUTEX); i–; if(i==0){ signal(access); } release(MUTEX);
```cpp
acquire(MUTEX);
while( i != 0){
wait(access);
}
i--;
release(MUTEX);
write database;
acquire(MUTEX);
i = 0;
broadCast(access); //Reader와 Writer 모두 깨운다.
release(MUTEX);