進程同步 synchronization
描述
指系統中多個進程中發生的時間存在某種時序關關係,需要相互合作,共同完成一項任務。
場景
一個進程運行到某一點時,要求另一個夥伴進程爲它提供消息,在未獲得消息之前,該進程進入阻塞態,獲得消息後被喚醒進入就緒態。*
生產者與消費者問題又稱爲有界緩衝區問題
場景:
- 一個或者多個生產者生產某種類型的數據放置在緩衝區中
- 有消費者從緩衝區中取數據,每次取一項
- 只能有一個生產者貨消費者對緩衝區進行操作
要解決的問題:
- 當緩衝區已滿時,生產者不會繼續向其中添加數據
- 當緩衝區爲空時,消費者不會從中移走數據
- 要避免忙等待,睡眠和喚醒操作(原語)
#define N 100
int count = 0;
void producer(void)
{
int item;
while(TRUE)
{
item = produce_item();
if(count == N) /*判斷緩衝區是否滿了*/
sleep();
insert_item(item);
count = count + 1;
if(count == 1) /*判斷是否有消費者睡眠*/
wakeup(consumer);
}
}
void consumer(void)
{
int item;
while(TRUE)
{
if(count == 0) /*判斷是否有數據*/
sleep();
item = remove_item();
count = count - 1;
if(count == N-1) /*判斷是否有生產者睡眠*/
wakeup(producer);
consume_item(item);
}
}
一種經典的進程同步機制–信號量及P、V操作
信號量及P、V操作
- 一個特殊變量
- 用於進程間傳遞信息的一個整數值
- 定義
struct semaphore
{
int count;
queueType queue;
}
- 信號量說明: semaphore s;
- 對信號量可以實施的操作:初始化、P和V(P、V是指荷蘭語中的test(porberen)和increment(verhogen))
- P、V操作爲原語操作(Primitive or atomic action)
- 在信號量上定義了三個操作初始化(非負數)、P操作、V操作
- 最初提出的是二元信號量(解決互斥)之後推廣到一般信號量(多值)或計數信號量(解決同步)
P(s)
{
s.count--;
if(s.count < 0)
{
該進程狀態置爲阻塞狀態;
該進程插入相應的等待隊列s.queue末尾;
重新調度;
}
}
V(s)
{
s.count++;
if(s.count <= 0)
{
喚醒相應的等待隊列s.queue中等待的一個進程;
改變其狀態爲就緒態,並將其插入就緒隊列;
}
}
用PV操作解決進程間的互斥問題
- 分析併發進程的關鍵活動,或定臨界區
- 設置信號量 mutex,初值爲1
- 在臨界區前實施P(mutex)
- 在臨界區之後實施V(mutex)
用信號量解決生產者、消費者問題
#define N 100 /*緩衝區個數*/
typedef int semaphore; /*信號量是一種特殊的整數類型*/
semaphore mutex = 1; /*互斥信號量:控制對臨界區的訪問*/
semaphore empty= N; /*空緩衝區的個數*/
semaphore full = 0; /*滿緩衝區個數*/
void producer(void)
{
int item;
while(TRUE)
{
item = produce_item()
P(&empty);
P(&mutex);
insert_item(item);
V(&mutex);
V(&full);
}
}
void consumer(void)
{
int item;
while(TRUE)
{
P(&full);
P(&mutex);
item = remove_item()
P(&mutex);
V(&empty);
consume_item(item);
}
}
producer中P(&empty);P(&mutex);中不能顛倒位置會造成死鎖問題。consumer中的V(&mutex);V(&full);V操作位置可以顛倒,不會出錯,但是現在最好,還有consume_item(item);放在臨界區內也行,不會出錯,但是會增加臨界區的發威,現在這種排序的臨界區範圍最小。