寫在前面:
轉載請注明本文鏈接:https://www.cnblogs.com/jxingh/p/15624566.html
PV 操作
1、生產者-消費者問題

semaphore empty = n; // 表示空閑緩沖區的數量
semaphore full = 0; // 表示滿緩沖區的數量
semaphore mutex = 1;
producer(){ // 生產者
while(1){
生產一個產品;
P(empty);
P(mutex);
向緩沖區放入該產品;
V(mutex);
V(full);
}
}
consumer(){
while(1){
P(full);
P(mutex);
從緩沖區取出一件產品;
V(mutex);
V(empty);
消費一個產品;
}
}
2、讀者-寫者問題
①允許多個讀者可以同時對文件執行讀操作;
②只允許一個寫者往文件中寫信息;
③任一寫者在完成寫操作之前不允許其他讀者或寫者工作;
④寫者執行寫操作前,應讓已有的讀者和寫者全部退出。
// 讀者優先
// 當存在讀進程時,寫操作將被延遲,並且只要有一個讀進程活躍,隨后而來的讀進程都將被允許訪問文件
// 會導致寫進程可能長時間等待,且存在寫進程“餓死”的情況
int readcount = 0; // 表示正在進行讀的讀者數目
semaphore rmutex = 1; // 實現多個讀者對readcount的互斥訪問
semaphore mutex = 1; // 實現對文件的互斥訪問
reader(){
while(1){
P(rmutex);
if(readcount==0) P(mutex); // 第一個讀者
readcount++;
V(rmutex);
讀操作;
P(rmutex);
readcount--;
if(readcount==0) V(mutex); // 最后一個讀者
V(rmutex);
}
}
writer(){
while(1){
P(mutex);
寫操作;
V(mutex);
}
}
// 讀寫公平
// 無論是讀者還是寫者,都按他們到達的時間先后決定優先次序
int readcount = 0;
semaphore rmutex = 1;
semaphore mutex = 1;
semaphore S = 1; // 實現先到的先進行讀或寫操作
reader(){
while(1){
P(S);
P(rmutex);
if(readcount==0) P(mutex);
readcount++;
V(rmutex);
V(S);
讀操作;
P(rmutex);
readcount--;
if(readcount==0) V(mutex);
V(rmutex);
}
}
writer(){
while(1){
P(S);
P(mutex);
V(S);
寫操作;
V(mutex);
}
}
// 寫者優先
// 先於寫者到達的讀者比寫者優先,但寫者到達后,其后續讀者必須等待寫操作完成
// 寫完成之前,有新的寫者到來,其優先權高於已在等待的讀者
int readcount = 0, writecount = 0;
semaphore rmutex = 1, wmutex = 0; // 分別實現讀者、寫者對readcount、writecount的互斥訪問
semaphore mutex = 1;
semaphore S = 1;
reader(){
while(1){
P(S);
P(rmutex);
if(readcount==0) P(mutex);
readcount++;
V(rmutex);
V(S);
讀操作;
P(rmutex);
readcount--;
if(readcount==0) V(mutex);
V(rmutex);
}
}
writer(){
while(1){
P(wmutex);
if(writecount==0) P(S); // 第一個寫者
writecount++;
V(wmutex);
P(mutex);
寫操作;
V(mutex);
P(wmutex);
writecount--;
if(writecount==0) V(S); // 最后一個寫者
V(wmutex);
}
}
// 寫者優先
// 寫者優先權更高。某個寫者到達后,即使前面沒有寫者,那先於他到達但還沒來得及讀的讀者必須等待他完成寫操作
int readcount = 0, writecount = 0;
semaphore rmutex = 1, wmutex = 0; // 分別實現讀者、寫者對readcount、writecount的互斥共享
semaphore mutex = 1;
semaphore S = 1;
semaphore RS = 1;
reader(){
while(1){
P(RS); // 避免讀者在S上排成長隊
P(S);
P(rmutex);
if(readcount==0) P(mutex);
readcount++;
V(rmutex);
V(S);
V(RS);
讀操作;
P(rmutex);
readcount--;
if(readcount==0) V(mutex);
V(rmutex);
}
}
writer(){
while(1){
P(wmutex);
if(writecount==0) P(S); // 第一個寫者
writecount++;
V(wmutex);
P(mutex);
P(mutex);
寫操作;
V(mutex);
P(wmutex);
writecount--;
if(writecount==0) P(S); // 最后一個寫者
V(wmutex);
}
}
3、哲學家問題
(1)至多只允許4個哲學家同時進餐。
(2)僅當哲學家左右兩邊的筷子都可用時,才允許他進餐。
(3)規定奇數號哲學家先拿左邊的筷子,再拿右邊的筷子;而偶數號哲學家則先拿右邊的筷子,再拿左邊的筷子。
semaphore chopstick[5] = {1, 1, 1, 1, 1};
(1)
semaphore count = 4; // 設置一個count,最多有四個哲學家可以進餐
philosopher(int i){
while(true){
P(count); // 請求進餐,當count為0時,不能允許哲學家再進餐
P(chopstick[i]); // 請求左手邊的筷子
P(chopstick[(i+1)%5]); // 請求右手邊的筷子
i號哲學家進餐;
V(chopstick[i]); // 釋放左手邊的筷子
V(chopstick[(i+1)%5]); // 釋放右手邊的筷子
V(count); // 結束進餐
}
}
(2)
semaphore mutex = 1; // 對取左側和右側筷子的操作進行保護
philosopher(int i){
while(true){
P(mutex); // 保護信號量
P(chopstick[(i+1)%5]);
P(chopstick[i]);
V(mutex); // 釋放保護信號量
i號哲學家進餐;
V(chopstick[(i+1)%5]);
V(chopstick[i]);
}
}
(3)
philosopher(int i){
while(true){
if(i%2 == 0){ //偶數哲學家,先右后左
P(chopstick[(i+1)%5]) ;
P(chopstick[i]) ;
i號哲學家進餐;
V(chopstick[(i+1)%5]) ;
V(chopstick[i]) ;
}
else{ //奇數哲學家,先左后右
P(chopstick[i]) ;
P(chopstick[(i+1)%5]) ;
i號哲學家進餐;
V(chopstick[i]) ;
V(chopstick[(i+1)%5]) ;
}
}
}
4、變形
(1)生產線問題
有三個進程PA、PB和PC合作解決⽂件打印問題。PA將⽂件記錄從磁盤讀⼊內存的緩沖區1,每執⾏⼀次讀⼀個記錄。PB將緩沖區1的內容復制到緩沖區2,每執⾏⼀次復制⼀個記錄。PC將緩沖區2的內容打印出來,每執⾏⼀次打印⼀個記錄。緩沖區的⼤⼩與記錄⼤⼩一樣。
semaphore empty1=1, full1=0, empty2=1, full2=0;
PA(){
while(1){
從磁盤讀一個記錄;
P(empty1);
將記錄存放在緩沖區1中;
V(full1);
}
}
PB(){
P(full1);
從緩沖區1中取出一個記錄;
V(empty1);
P(empty2);
將記錄復制到緩沖區1中;
V(full2);
}
PC(){
P(empty1);
從緩沖區1中取出一個記錄;
V(full1);
打印該記錄;
}
(2)緩沖區划分
進程A1、A2、......、An1通過m個緩沖區向進程B1、B2、......、Bn2不斷地發送消息。發送和接收工作遵循如下規則:
(1)每個發送進程一次發送一個消息,寫入一個緩沖區,緩沖區大小和消息長度相同;
(2)對每一個消息,B1、B2、.......、Bn2都需要各接收一次,讀入自己的數據區內;
(3)m個緩沖區都滿時,發送進程等待;沒有可讀的消息時,接收進程等待。
https://blog.csdn.net/silent_gods/article/details/84455122
(3)令牌問題
設有兩個生產者進程A、B和一個銷售者進程C,他們共享一個無限大的倉庫,生產者每次循環生產一個產品,然后入庫供銷售者銷售;銷售者每次循環從倉庫中取出一個產品進行銷售。如果不允許同時入庫,也不允許邊入庫邊出庫;而且要求生產A產品和B產品的件數滿足以下關系:-n<=A的件數-B的件數<=m,其中n、m是正整數,但對倉庫中A產品和B產品的件數無上述要求。
// A-B<=m B-A<=n
// 若只生產A,而不生產B,則A產品最多可生產m次便被阻塞
// 若只生產B,而不生產A,則B產品最多可生產n次便被阻塞;
// 每生產一次A,生產產品B的機會也多一次;同理,每放入一次B,生產產品A的機會也多一次
semaphore mutex=1; // 互斥信號量
semaphore S=0; // 當前倉庫中的產品數
semaphore SA=m; // 當前允許A生產的產品數
semaphore SB=n; // 當前允許B生產的產品數
LA=m LB=n
PA(){
while(1){
P(SA);
生產一個產品A;
V(SB);
P(mutex);
產品A入庫;
V(mutex);
V(S);
}
}
PB(){
while(1){
P(SB);
生產一個產品B;
V(SA);
P(mutex);
產品B入庫;
V(mutex);
V(S);
}
}
PC(){
while(1){
P(S);
P(mutex);
取走一個A或B產品;
V(mutex);
銷售產品;
}
}
// 思考:如果倉庫中產品數也要滿足該關系呢?
(4)理發師問題
一個理發店由一個有N張沙發的等候室和一個放有一張理發椅的理發室組成。沒有顧客要理發時,理發師便去睡覺。當一個顧客走進理發店時,如果所有的沙發都已被占用,他便離開理發店;否則,如果理發師正在為其他顧客理發,則該顧客就找一張空沙發坐下等待;如果理發師因無顧客正在睡覺,則由新到的顧客喚醒理發師為其理發。在理發完成后,顧客必須付費,直到理發師收費后才能離開理發店。
int count=0; // 沙發上人數
semaphore mutex; // 實現不同顧客對count的互斥共享
semaphore empty=1,full=0; // 理發椅是否空閑
semaphore payment=0,receipt=0; // 等待付費、收費
guest(){
P(mutex);
if(count>=N){
V(mutex);
離開理發店;
}else{
count++;
V(mutex);
在沙發等待;
P(empty); // 等待理發椅變空
離開沙發,坐到理發椅上;
P(mutex);
count--;
V(mutex);
V(full); // 喚醒理發師
理發;
付費;
V(payment); // 通知理發師收費
P(receipt); // 等待理發師收費
V(empty); // 離開理發椅
離開理發店;
}
}
Barber(){
while(1){
P(full); // 等待理發椅來人
給顧客理發;
P(payment); // 等待顧客付費
收費;
V(receipt); // 通知顧客已收費
}
}
(5)獨木橋問題
同一方向的行人可連續過橋,當某一方向有人過橋時,另一方向的行人必須等待;當某一方向無人過橋時,另一方向的行人可以過橋。
int countA=0, countB=0; // 分別表示A、B兩個方向過橋的人數
semaphore mutex=1; // 不同方向行人互斥過橋
semaphore mutexA=1, mutexB=1; // 分別實現對countA、countB的互斥訪問
PA(){
while(1){
P(mutexA);
if(countA==0) P(mutex); // 第一個過橋
countA++;
V(mutexA);
過橋;
P(mutexA);
countA--;
if(countA==0) V(mutex); // 最后一個過橋
V(mutexA);
}
}