讀者-寫者問題 寫者優先與公平競爭
設有一檔案F,多個併發讀程序和寫程序都要訪問,要求:
(1)讀寫互斥
(2)寫寫互斥
(3)允許多個讀程序同時訪問
採用記錄型訊號量機制解決
較常見的寫法:
semaphore fmutex=1, rdcntmutex=1;
//fmutex --> access to file; rdcntmutex --> access to readcount
int readcount = 0;
void reader(){
while(1){
wait(rdcntmutex);
if(0 == readcount)wait(fmutex);
readcount = readcount + 1;
signal(rdcntmutex);
//Do read operation ...
wait(rdcntmutex);
readcount = readcount - 1;
if(0 == readcount)signal(fmutex);
signal(rdcntmutex);
}
}
void writer(){
while(1){
wait(fmutex);
//Do write operation ...
signal(fmutex);
}
}
讀程序只要看到有其他讀程序正在訪問檔案,就可以繼續作讀訪問;寫程序必須等待所有讀程序都不訪問時才能寫檔案,即使寫程序可能比一些讀程序更早提出申請。所以以上解法實際是讀者優先 的解法。如果在讀訪問非常頻繁的場合,有可能造成寫程序一直無法訪問檔案的局面....
為了解決以上問題,需要提高寫程序的優先順序。這裡另增加一個排隊訊號量:queue。讀寫程序訪問檔案前都要在此訊號量上排隊,通過區別對待讀寫程序便可達到提高寫程序優先順序的目的。另外再增加一個 writecount 以記錄提出寫訪問申請和正在寫的程序總數:
semaphore fmutex=1, rdcntmutex=1, wtcntmutex=1, queue=1;
//fmutex --> access to file; rdcntmutex --> access to readcount
//wtcntmutex --> access to writecount
int readcount = 0, writecount = 0;
void reader(){
while(1){
wait(queue);
wait(rdcntmutex);
if(0 == readcount)wait(fmutex);
readcount = readcount + 1;
signal(rdcntmutex);
signal(queue);
//Do read operation ...
wait(rdcntmutex);
readcount = readcount - 1;
if(0 == readcount)signal(fmutex);
signal(rdcntmutex);
}
}
void writer(){
while(1){
wait(wtcntmutex);
if(0 == writecount)wait(queue);
writecount = writecount + 1;
signal(wtcntmutex);
wait(fmutex);
//Do write operation ...
signal(fmutex);
wait(wtcntmutex);
writecount = writecount - 1;
if(0 == writecount)signal(queue);
signal(wtcntmutex);
}
}
每個讀程序最開始都要申請一下 queue 訊號量,之後在真正做讀操作前即讓出(使得寫程序可以隨時申請到 queue)。而只有第一個寫程序需要申請 queue,之後就一直佔著不放了,直到所有寫程序都完成後才讓出。等於只要有寫程序提出申請就禁止讀程序排隊,變相提高了寫程序的優先順序。
通過類似思想即可實現讀寫程序的公平競爭:
semaphore fmutex=1, rdcntmutex=1, queue=1;
//fmutex --> access to file; rdcntmutex --> access to readcount
int readcount = 0;
void reader(){
while(1){
wait(queue);
wait(rdcntmutex);
if(0 == readcount)wait(fmutex);
readcount = readcount + 1;
signal(rdcntmutex);
signal(queue);
//Do read operation ...
wait(rdcntmutex);
readcount = readcount - 1;
if(0 == readcount)signal(fmutex);
signal(rdcntmutex);
}
}
void writer(){
while(1){
wait(queue);
wait(fmutex);
signal(queue);
//Do write operation ...
signal(fmutex);
}
}
讀程序沒變,寫程序變成在每次寫操作前都要等待 queue 訊號量。
課本上一般只會寫第一種解法吧。看了後兩種方法即可發現,在第一個解法中,fmutex 訊號量實際是雙重身份,首先實現對檔案的互斥訪問,其次起到了和後面排隊訊號量 queue 相同的作用,只不過在那種排序下只能是讀者優先。如果直接看過後兩種解法,應該會有更清楚的理解吧。