一、爲什麼要引入管程
爲了實現進程的同步,互斥操作,我們引入了信號量機制。
但信號量機制存在問題:我們編寫程序困難,容易出錯。
於是爲了方便實現進程的同步,互斥操作提出了管程。
二、管程的組成和基本特性
1、管程的組成
2、管程的基本特性
1)局部於管程的數據只能被局部於管程內的過程所訪問
2)一個進程只有通過調用管程內的過程才能進入管程訪問共享數據
3)每次僅允許一個進程在管程內執行某個內部過程
三、用管程解決生產者消費者問題
//管程
monitor ProducerConsumer
condition full,empty; //條件變量用來實現同步(排隊)
int count=0; //緩衝區中的產品數
void insert(Item item) //把產品item放入緩衝區
{
if(count == N)
wait(full); //緩衝區滿了,自動堵塞
count++;
inset_item(item);
if(count == 1) //如果這個產品放入緩衝區後,共有一個產品,則可能有消費者進程在等待
signal(empty) //喚醒empty隊列的隊頭消費者進程
}
Item remove()
{
if(count == 0) //說明緩衝區中無產品
wait(empty); //消費者進程自動堵塞,進入empty隊列等候
count--;
if(count == N-1) //緩衝區有空閒了
signal(full); //喚醒full隊列中的生產者進程
}
end monitor;
//生產者進程
Producer()
{
while(1)
{
item = 生產的產品;
ProducerConsumer.insert(item);
}
}
//消費者進程
Producer()
{
while(1)
{
ProducerConsumer.remove();
消費產品item;
}
}
由於每次只允許一個進程在管程內執行每個內部過程,所以由編譯器負責實現各個進程互斥的進入管程的過程
總而言之,引入管程無非就是更方便地實現進程互斥和同步。
1)需要在管程中定義共享數據(如生產者消費者進程中的緩衝區)
2)需要在管程中定義用於訪問這些數據的入口——其實就是一些函數(如生產者消費者進程中,可以定義一個函數將產品放入緩衝區,在定義一個函數將產品從緩衝區取出)
3)只有通過這些特定的入口才能訪問數據
4)管程中有很多入口(函數),但是每次只能開放一個入口(函數),並且只讓一個進程進入,這樣就實現了進程的互斥。注:這種特性由編譯器實現程序員不需要關心
5)可以在管程中設置條件變量及等待/喚醒操作來解決同步問題。可以讓一個進程或者線程在條件變量上等待(此時,該進程應該釋放管程的使用權,也就是讓出函數入口);可以通過喚醒操作將等待在變量上的進程或線程喚醒
四、Java中類似於管程的機制
在Java中,如果使用關鍵字synchronize來描述一個函數,那麼這個函數同一時間只能被一個進程調用。
如果多個線程同時調用函數,則後來者需要排隊等待。
static class monitor{
private Iteam buffer[] = new Item[N];
private int count=0;
public synchronize void insert(Item item){
.......
}
}