條件變量
條件變量的引入是為了解決互斥鎖中的循環(huán)等待問題,其希望引入一種掛起综膀、喚醒的機(jī)制來實(shí)現(xiàn)cpu的高效利用
使用條件變量解決生產(chǎn)者消費(fèi)者問題:
int empty_slot = 5;
int filled_slot = 0;
struct cond empty_cond;//緩沖區(qū)無空位(滿)
struct lock empty_cnt_lock;//互斥鎖
struct cond filled_cond;//緩沖區(qū)無數(shù)據(jù)(空)
struct lock filled_cnt_lock;//互斥鎖
void producer(void)
{
int new_msg;
while(TRUE){
new_msg = prodece_new();
lock(&empty_cnt_lock);//加鎖澳迫,進(jìn)入臨界區(qū)
while(empty_slot == 0)//緩沖區(qū)無空槽
//掛起當(dāng)前線程,并釋放互斥鎖(原子性)剧劝,其中兩個(gè)形參分別為條件變量和互斥鎖
cond_wait(&empty_cond,&empty_cnt_lock);//等待slot>0橄登,故empty_cond
empty_slot--;
unlock(&empty_cnt_lock);
buffer_add_safe(new_msg);//利用互斥鎖來生產(chǎn)
lock(&filled_cnt_lock);
filled_slot++;
cond_signal(&filled_cond);//一般放在臨界區(qū)內(nèi)喚醒
unlock(&filled_cnt_lock);
}
}
void consumer(void)
{
int cur_msg;
while(TRUE){
lock(&filled_cnt_lock);//加鎖,進(jìn)入臨界區(qū)
while(filled_slot == 0)//緩沖區(qū)無數(shù)據(jù)
//掛起當(dāng)前線程讥此,并釋放互斥鎖(原子性)拢锹,其中兩個(gè)形參分別為條件變量和互斥鎖
cond_wait(&filled_cond,&filled_cnt_lock);
filled_slot--;
unlock(&filled_cnt_lock);
cur_msg = buffer_remove_safe();
lock(&empty_cnt_lock);
empty_slot++;
cond_signal(&empty_cond);//喚醒等待在此變量上的生產(chǎn)者
unlock(&empty_cnt_lock);
consume_msg(cur_msg);
}
}
條件變量的實(shí)現(xiàn)中要注意,當(dāng)線程調(diào)用cond_wait()時(shí)需要將當(dāng)前線程加入等待隊(duì)列并原子地掛起當(dāng)前線程并釋放鎖
void cond_wait(條件變量萄喳,互斥鎖){
加入條件變量的等待隊(duì)列
原子掛起并放互斥鎖
重新獲得鎖
}
信號(hào)量semaphore [?sem?f??r]
Dijkstra提出了信號(hào)量機(jī)制卒稳,并只可以通過P(Proberen 檢驗(yàn)) V(Verhogen 自增)操作來進(jìn)行更新信號(hào)量,一般分別用wait和singal表示他巨。wait檢查信號(hào)量的值充坑,小于等于0就循環(huán)等待,大于0就消耗資源染突,即減少信號(hào)量的值捻爷。signal會(huì)增加信號(hào)量的值供wait使用。
信號(hào)量解決生產(chǎn)者消費(fèi)者問題:
sem_t empty_slot;//空閑的緩沖區(qū)資源
sem_t filled_slot;//已經(jīng)填入的資源
void producer(){
int new_msg;
while(TRUE){
new_msg = produce_new();
wait(&empty_slot);//P操作
buffer_add_safe(new_msg);//利用互斥鎖進(jìn)行安全增加信息
signal(&filled_slot);//V操作
}
}
void consumer(){
int cur_msg;
while(TRUE){
wait(&filled_slot);//P操作
cur_msg = buffer_remove_safe();//利用互斥鎖進(jìn)行安全消耗信息
signal(&empty_slot);//V操作
consume(cur_msg);
}
}
信號(hào)量實(shí)現(xiàn)時(shí)存在一些問題:當(dāng)多個(gè)線程共享信號(hào)量時(shí)份企,如果不進(jìn)行原子更新信號(hào)量就會(huì)出現(xiàn)某個(gè)線程更新了舊值也榄,類似于CPU cache對(duì)不同線程的不可見問題。其次司志,就算原子更新甜紫,如果出現(xiàn)多個(gè)線程同時(shí)對(duì)信號(hào)量進(jìn)行減少操作降宅,會(huì)出現(xiàn)負(fù)值。
為了解決以上的問題囚霸,我們需要利用互斥鎖和條件變量來實(shí)現(xiàn)信號(hào)量
struct sem{
int value;//沒有線程等待時(shí)钉鸯,value>=0,為剩余資源數(shù);有線程等待則為等待的線程數(shù)
int wakeup;//有線程等待時(shí)可用資源數(shù)
struct lock sem_lock;
struct cond sem_cond;
}
void wait(struct sem *S){
lock(&S->sem_lock);
S->value--;
if(S->value < 0){
do{
cond_wait(&S->sem_cond, &S->sem_lock);
}while(S->wakeup == 0);
S->wakeup--;
}
unlock(&S->sem_lock);
}
void signal(struct sem *S){
lock(&S->sem_lock);
S->value++;
if(S->value <= 0){
S->wakeup++;
cond_signal(&S->sem_cond);
}
unlock(&S-sem_lock);
}
進(jìn)入wait后首先將value-1邮辽,如果值>=0唠雕,則代表還有空閑資源,直接unlock吨述,如果小于0就查看wakeup岩睁,如果wakeup為0,就掛起線程揣云。
互斥鎖與信號(hào)量的異同
當(dāng)信號(hào)量的初值為1捕儒,且在0和1之間變化時(shí),成為二元信號(hào)量邓夕。其與互斥鎖的區(qū)別在于:互斥鎖有擁有者這一概念刘莹,信號(hào)量則沒有》俑眨互斥鎖由同一線程加放鎖点弯,信號(hào)量可以由不同線程進(jìn)行PV操作。
計(jì)數(shù)信號(hào)量允許多個(gè)線程矿咕,且值為剩余可用資源數(shù)量抢肛。互斥鎖保證多個(gè)線程對(duì)一個(gè)共享資源的互斥訪問碳柱,信號(hào)量用于協(xié)調(diào)多個(gè)線程對(duì)一系列資源的訪問
條件變量與信號(hào)量的異同
信號(hào)量利用條件變量捡絮、互斥鎖、計(jì)數(shù)器實(shí)現(xiàn)莲镣,計(jì)數(shù)器就是信號(hào)量的核心福稳,信號(hào)量是條件變量的高級(jí)抽象