操作系统:经典进程同步问题 之 生产者-消费者问题、读者-写者问题、哲学家进餐问题
?
在進(jìn)程同步中,經(jīng)典的同步問題有:生產(chǎn)者-消費(fèi)者問題、讀者-寫者問題、哲學(xué)家進(jìn)餐問題。
一、生產(chǎn)者與消費(fèi)者問題:
問題描述:使用一個(gè)緩沖區(qū)來保存物品,只有緩沖區(qū)沒有滿,生產(chǎn)者才可以放入物品;只有緩沖區(qū)不為空,消費(fèi)者才可以拿走物品。
1、使用信號(hào)量實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者問題:
down?: 如果信號(hào)量大于 0 ,執(zhí)行 -1 操作;如果信號(hào)量等于 0,進(jìn)程睡眠,等待信號(hào)量大于 0;
up?:對(duì)信號(hào)量執(zhí)行 +1 操作,喚醒睡眠的進(jìn)程讓其完成 down 操作。
因?yàn)榫彌_區(qū)屬于臨界資源,因此需要使用一個(gè)互斥量 mutex 來控制對(duì)緩沖區(qū)的互斥訪問。
為了同步生產(chǎn)者和消費(fèi)者的行為,需要記錄緩沖區(qū)中物品的數(shù)量。數(shù)量可以使用信號(hào)量來進(jìn)行統(tǒng)計(jì),這里需要使用兩個(gè)信號(hào)量:empty 記錄空緩沖區(qū)的數(shù)量,full 記錄滿緩沖區(qū)的數(shù)量。其中,empty 信號(hào)量是在生產(chǎn)者進(jìn)程中使用,當(dāng) empty 不為 0 時(shí),生產(chǎn)者才可以放入物品;full 信號(hào)量是在消費(fèi)者進(jìn)程中使用,當(dāng) full 信號(hào)量不為 0 時(shí),消費(fèi)者才可以取走物品。
注意,不能先對(duì)緩沖區(qū)進(jìn)行加鎖,再測(cè)試信號(hào)量。也就是說,不能先執(zhí)行 down(mutex) 再執(zhí)行 down(empty)。如果這么做了,那么可能會(huì)出現(xiàn)這種情況:生產(chǎn)者對(duì)緩沖區(qū)加鎖后,執(zhí)行 down(empty) 操作,發(fā)現(xiàn) empty = 0,此時(shí)生產(chǎn)者睡眠。消費(fèi)者不能進(jìn)入臨界區(qū),因?yàn)樯a(chǎn)者對(duì)緩沖區(qū)加鎖了,消費(fèi)者就無法執(zhí)行 up(empty) 操作,empty 永遠(yuǎn)都為 0,導(dǎo)致生產(chǎn)者永遠(yuǎn)等待下,不會(huì)釋放鎖,消費(fèi)者因此也會(huì)永遠(yuǎn)等待下去。
#define N 100 typedef int semaphore; semaphore mutex = 1; semaphore empty = N; semaphore full = 0;void producer() {while(TRUE) {int item = produce_item();down(&empty);down(&mutex);insert_item(item);up(&mutex);up(&full);} }void consumer() {while(TRUE) {down(&full);down(&mutex);int item = remove_item();consume_item(item);up(&mutex);up(&empty);} }2、使用管程實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者問題:
?// 管程
?monitor ProducerConsumer
? ? condition full, empty;
? ? integer count := 0;
? ? condition c;
? ? procedure insert(item: integer);
? ? begin
? ? ? ? if count = N then wait(full);
? ? ? ? insert_item(item);
? ? ? ? count := count + 1;
? ? ? ? if count = 1 then signal(empty);
? ? end;
? ? function remove: integer;
? ? begin
? ? ? ? if count = 0 then wait(empty);
? ? ? ? remove = remove_item;
? ? ? ? count := count - 1;
? ? ? ? if count = N -1 then signal(full);
? ? end;
end monitor;
// 生產(chǎn)者客戶端
procedure producer
begin
? ? while true do
? ? begin
? ? ? ? item = produce_item;
? ? ? ? ProducerConsumer.insert(item);
? ? end
end;
// 消費(fèi)者客戶端
procedure consumer
begin
? ? while true do
? ? begin
? ? ? ? item = ProducerConsumer.remove;
? ? ? ? consume_item(item);
? ? end
end;
?
二、讀者-寫者問題:
允許多個(gè)進(jìn)程同時(shí)對(duì)數(shù)據(jù)進(jìn)行讀操作,但是不允許讀和寫以及寫和寫操作同時(shí)發(fā)生。
一個(gè)整型變量 count 記錄在對(duì)數(shù)據(jù)進(jìn)行讀操作的進(jìn)程數(shù)量,一個(gè)互斥量 count_mutex 用于對(duì) count 加鎖,一個(gè)互斥量 data_mutex 用于對(duì)讀寫的數(shù)據(jù)加鎖。
typedef int semaphore; semaphore count_mutex = 1; semaphore data_mutex = 1; int count = 0;void reader() {while(TRUE) {down(&count_mutex);count++;if(count == 1) down(&data_mutex); // 第一個(gè)讀者需要對(duì)數(shù)據(jù)進(jìn)行加鎖,防止寫進(jìn)程訪問up(&count_mutex);read();down(&count_mutex);count--;if(count == 0) up(&data_mutex);up(&count_mutex);} }void writer() {while(TRUE) {down(&data_mutex);write();up(&data_mutex);} }?
三、哲學(xué)家進(jìn)餐問題:
五個(gè)哲學(xué)家圍著一張圓桌,每個(gè)哲學(xué)家面前放著食物。哲學(xué)家的生活有兩種交替活動(dòng):吃飯以及思考。當(dāng)一個(gè)哲學(xué)家吃飯時(shí),需要先拿起自己左右兩邊的兩根筷子,并且一次只能拿起一根筷子。
下面是一種錯(cuò)誤的解法,考慮到如果所有哲學(xué)家同時(shí)拿起左手邊的筷子,那么就無法拿起右手邊的筷子,造成死鎖。
#define N 5void philosopher(int i) {while(TRUE) {think();take(i); // 拿起左邊的筷子take((i+1)%N); // 拿起右邊的筷子eat();put(i);put((i+1)%N);} }為了防止死鎖的發(fā)生,可以設(shè)置兩個(gè)條件:
- 必須同時(shí)拿起左右兩根筷子;
- 只有在兩個(gè)鄰居都沒有進(jìn)餐的情況下才允許進(jìn)餐。
?
?
文章轉(zhuǎn)自:https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.md#%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8
總結(jié)
以上是生活随笔為你收集整理的操作系统:经典进程同步问题 之 生产者-消费者问题、读者-写者问题、哲学家进餐问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Maven(二):常用命令、依赖管理
- 下一篇: JUC多线程:创建线程的四种方式