1. 程式人生 > >有關程序同步與互斥的經典問題

有關程序同步與互斥的經典問題

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);
    }
}