有關程序同步與互斥的經典問題
阿新 • • 發佈:2018-11-21
1生產者消費者問題
1.1使用二元訊號量解決無限緩衝區的生產者消費者問題
//使用二元訊號量解決無限緩衝區的生產者消費者問題 int count = 0; //count為緩衝區中的資料項個數 BinSem s = 1, delay = 0; //s為二元訊號量,控制生產者和消費者進入臨界區; //delay為二元訊號量,處理緩衝區為空的情況; void producer(){ while(1){ produce(); semWaitB(s); append(); count++; if(count==1)semSignalB(delay); //緩衝區非空,喚醒消費者程序 semSignalB(s); } } void consumer(){ semWaitB(delay); while(1){ semWaitB(s); take(); count--; m = count; semSignalB(s); consume(); //用到m是因為此時在臨界區外,在執行下面一條語句之前count可能已經被更新,導致delay不匹配 if(m==0)semWaitB(delay); //緩衝區已空,阻塞消費者程序 } }
1.2使用計數訊號量解決無限緩衝區的生產者消費者問題
//使用計數訊號量解決無限緩衝區的生產者消費者問題 sem count = 0; //count為訊號量,值等於緩衝區資料項的個數; BinSem s = 1; //s為二元訊號量,控制生產者消費者進入臨界區 void producer(){ while(1){ produce(); semWaitB(s); append(); //下面兩行互換沒問題,因為消費者必須在兩個訊號量上等待 semSignalB(s); semSignal(count); //緩衝區非空 } } void consumer(){ while(1){ //下面兩行互換會死鎖,如果緩衝區為空時,消費者進入臨界區,會因為緩衝區為空被阻塞 //但是又沒有釋放二元訊號量s,造成生產者無法進入臨界區 semWaitB(count); semWait(s); take(); semSignalB(s); consume(); } }
1.3使用計數訊號量解決有限緩衝區的生產者消費者問題
//使用計數訊號量解決有限緩衝區的生產者消費者問題 const int sizeofbuffer = 100; sem count = 0; //count為訊號量,值等於緩衝區資料項的個數; sem avail = sizeofbuffer; //avail為訊號量,記錄空閒空間的數目 BinSem s = 1; //s為二元訊號量,控制生產者消費者進入臨界區 void producer(){ while(1){ produce(); //下面兩行互換會死鎖,如果緩衝區滿時,生產者進入臨界區,會因為緩衝區滿被掛起 //但是又沒有釋放二元訊號量s,造成消費者無法進入臨界區 semWait(avial); semWaitB(s); append(); //下面兩行互換沒問題,因為消費者必須在兩個訊號量上等待 semSignalB(s); semSignal(count); //緩衝區非空 } } void consumer(){ while(1){ //下面兩行互換會死鎖,如果緩衝區為空時,消費者進入臨界區,會因為緩衝區為空被掛起 //但是又沒有釋放二元訊號量s,造成生產者無法進入臨界區 semWaitB(count); semWait(s); take(); //下面兩行互換沒問題,因為生產者必須在兩個訊號量上等待 semSignalB(s); semSignal(avail); //緩衝區沒滿 consume(); } }
1.4使用管程解決有限緩衝區的生產者消費者問題
//使用管程解決有限緩衝區的生產者消費者問題
monitor boundedbuffer; //建立管程
const int N = 100;
char buffer[N];
int nextin,nextout; //緩衝區指標
int count; //緩衝區中資料項的個數
cond notfull,notempty; //為同步設定的條件變數
void append(char x){
if(count==N)cwait(notfull); //緩衝區滿,防止溢位
buffer[nextin]=x;
nextin = (nextin+1)%N;
count++;
csignal(notempty); //釋放任何一個等待的程序
}
void take(){
if(count==0)cwait(notempty); //緩衝區空,防止下溢
x = buffer[nextout];
nextout = (nextout+1)%N;
count--;
csignal(notfull); //釋放任何一個等待的程序
}
cmonitor(){ //管程體
//緩衝區初始化為空
nextin = 0;
nextout = 0;
count = 0;
}
void producer(){
char x;
while(1){
produce(x);
append(x);
}
}
void consumer(){
char x;
while(1){
take(x);
consume(x);
}
}
1.5使用訊息解決有限緩衝區生產者消費者問題
//使用訊息解決有限緩衝區生產者消費者問題
const int capacity = 100; //緩衝區容量
void producer(){
message pmsg;
while(1){
receive(mayproduce,pmsg); //收到空訊息,可以生產資料
pmsg = produce();
send(mayconsume,pmsg); //傳送生產的訊息
}
}
void consumer(){
message cmsg;
while(1){
receive(mayconsume,cmsg); //收到訊息,可以消費資料
consume(cmsg);
send(mayproduce,null); //傳送空訊息
}
}
void main(){
create_mailbox(mayproduce);
create_mailbox(mayconsume);
for(int i=0;i<capacity;i++){
send(mayproduce,null); //最初mayproduce信箱充滿了空訊息
}
parbegin(producer,consumer);
}
2讀者寫者問題
2.1讀者優先
//讀者寫者問題,讀者優先
int readcount = 0; //記錄讀程序的數目
BinSem x = 1, w = 1; //x用於控制readcout被正確地更新;w用於寫程序與其他程序的互斥
void writer(){
while(1){
semWaitB(w);
write();
semSignalB(w);
}
}
void reader(){
while(1){
//來了一個讀者
semWaitB(x);
readcount++;
if(readcount==1)semWaitB(w); //讀者進入臨界區
semSignal(x);
read();
//走了一個讀者
semWaitB(x);
readcount--;
if(readcount==0)semSignal(w); //讀者出臨界區
semSignal(x);
}
}
2.2寫者優先
//讀者寫者問題,寫者優先
int readcount = 0, writecount = 0; //讀者程序數目和寫者程序數目
BinSem x = 1, y = 1, z = 1, w = 1, r = 1;
//x控制readcount的更新,y控制writecount的更新,z防止r上出現長佇列
//w控制寫程序與其他程序的互斥,r用於當一個寫程序準備訪問資料時,禁止所有的讀程序
void reader(){
while(1){
//來了一個讀程序
semWaitB(z);
semWaitB(r);
semWaitB(x);
readcount++;
if(readcount==1)semWaitB(w); //禁止寫
semSignalB(x);
semSignalB(r);
semSignalB(z);
read();
//走了一個讀程序
semWaitB(x);
readcount--;
if(readcount==0)semSignalB(w); //允許寫
semWaitB(x);
}
}
void writer(){
while(1){
//來了一個寫程序
semWaitB(y);
writecount++;
if(writecount==1)semWaitB(r); //禁止讀
semSignalB(y);
semWaitB(w);
write();
semSignalB(w);
//走了一個寫程序
semWaitB(y);
writecount--;
if(writecount==0)semSignalB(r); //允許讀
semWaitB(y);
}
}
2.3使用訊息解決讀者寫者問題
//使用訊息解決讀者-寫者問題
int count = 100;
void reader(int i){
message rmsg;
while(1){
rmsg = i;
send(readrequest,rmsg);
receive(mbox[i],rmsg);
read();
rmsg = i;
send(finished,rmsg);
}
}
void writer(int j){
message rmsg;
while(1){
rmsg = j;
send(writerequest,rmsg);
receive(mbox[j],rmsg);
write();
write();
rmsg = j;
send(finished,rmsg);
}
}
void controller(){
while(1){
if(count<0){ //沒有讀程序正在等待
if(!empty(finished)){
rceive(finished,msg);
count++;
}else if(!empty(writerequest)){
receive(writerequest,msg);
writer_id = msg.id;
count-=100;
}else if(!empty(readrequest)){
receive(readquest,msg);
count--;
send(msg.id,"OK");
}
}
if(count==0){ //唯一未解決的請求是寫請求
send(writer_id,"OK");
receive(finished,msg);
count = 100;
}
while(count<0){ //寫程序已經發出一條請求:
receive(finished,msg);
count++;
}
}
}
3哲學家就餐問題
3.1利用訊號量解決哲學家就餐問題
//利用訊號量解決哲學家就餐問題
sem fork[5] = {1,1,1,1,1};
sem room = 4; //一次只允許四個哲學家進入餐廳用餐,故肯定有一個哲學家能拿到兩個叉子
void phylosopher(int i){
while(1){
think();
wait(room);
wait(fork[i]);
wait(fork[(i+1)%5]); //如果所有哲學家同時先拿起左邊的叉子,再拿起右邊的叉子,則會死鎖,解決方法是加個訊號量room
eat();
signal(fork[i+1]%5);
signal(fork[i]);
signal(room);
}
}
3.2利用管程解決哲學家就餐問題
//利用管程解決哲學家就餐問題
monitor dining_controller;
cond ForkReady[5]; //condition variable for synchronization
bool fork[5] = {true};
void get_forks(int pid){
int left = pid;
int right = [++pid]%5;
if(!fork[left])
cwait(ForkReady[ready]);
fork(left) = false;
if(!fork[right])
cwait(ForkReady[ready]);
fork(right) = false;
}
void release_forks(int pid){
int left = pid;
int right = [++pid]%5;
if(empty(fork[left])) //no one is waiting for this fork
fork[left] = true;
else
csignal(ForkReady[left]); //awaken a process waiting for this fork
if(empty(fork[right]))
fork[right] = true;
else
csignal(ForkReady[right]);
}
void phylosopher(int k){
while(1){
think();
get_forks(k);
eat();
release_forks(k);
}
}