熱線(xian)電(dian)話:0755-23712116
郵箱:contact@legoupos.cn
地址:深圳市(shi)寶安區(qu)沙井街道后亭茅(mao)洲山工業園工業大廈全至科(ke)技創(chuang)新園科(ke)創(chuang)大廈2層2A
當某個(ge)線(xian)程(cheng)持有(you)這(zhe)把鎖的時候(hou)(就是(shi)所謂的加鎖),那(nei)么(me)這(zhe)個(ge)線(xian)程(cheng)是(shi)獨占所有(you)的資(zi)源,這(zhe)里的資(zi)源指的是(shi)執行的權限,其他要搶奪資(zi)源的線(xian)程(cheng)都不(bu)得不(bu)等待。在很多情況(kuang)下(xia),這(zhe)都容(rong)易(yi)適用,但是(shi)有(you)些(xie)情況(kuang)下(xia),卻會產生一些(xie)異(yi)常情況(kuang)。
在(zai)生產(chan)消費(fei)者模型當(dang)中,肯定都會用到(dao)互(hu)斥鎖(suo)的(de)(de)機制的(de)(de),當(dang)生產(chan)者往(wang)隊(dui)列中放數(shu)據(ju)的(de)(de)瞬間(jian),消費(fei)者是(shi)不能(neng)取數(shu)據(ju)的(de)(de),那這時候(hou)可(ke)能(neng)會碰見一個問題,如果生成者因(yin)為(wei)某些(xie)原因(yin),放數(shu)據(ju)過慢,但是(shi)消費(fei)者取數(shu)據(ju)很快,當(dang)隊(dui)列中沒(mei)有(you)數(shu)據(ju)了(le),消費(fei)者還去取的(de)(de)話,就會發生異常情況。有(you)些(xie)人(ren)可(ke)能(neng)會說,加個條(tiao)件(jian)判斷一下隊(dui)列是(shi)否(fou)為(wei)空(kong)不就可(ke)以了(le)。
這個肯定是(shi)當然可以(yi)的(de)(de),但是(shi)在(zai)隊列(lie)依舊沒(mei)有數(shu)據的(de)(de)這一段時間,是(shi)要(yao)不(bu)斷(duan)的(de)(de)循環判斷(duan)這個條件,CPU肯定是(shi)會飆升的(de)(de),浪費了(le)很多不(bu)必要(yao)的(de)(de)資源。
這(zhe)時候我們設(she)想,能(neng)否設(she)計這(zhe)樣(yang)的(de)一(yi)種(zhong)機制,如果(guo)在隊列沒(mei)有數據(ju)的(de)時候,消費者(zhe)線程(cheng)能(neng)一(yi)直阻塞在那(nei)里,等待(dai)著別人給它(ta)喚醒,在生產者(zhe)往隊列中放入(ru)數據(ju)的(de)時候通知(zhi)一(yi)下(xia)這(zhe)個等待(dai)線程(cheng),喚醒它(ta),告訴它(ta)可以來(lai)取數據(ju)了。
于是多線程中的條件(jian)變量就橫(heng)空出世!
條(tiao)件變量是(shi)(shi)多(duo)線程(cheng)數據同步的一種(zhong)操作,不(bu)管(guan)是(shi)(shi)用哪種(zhong)框架,哪種(zhong)語言實現多(duo)線程(cheng)的功能,條(tiao)件變量都(dou)是(shi)(shi)不(bu)得(de)不(bu)考慮的一種(zhong)情(qing)況(kuang)。C++中(zhong)提供了(le)#include <condition_variable>頭文件,里面就(jiu)包含了(le)條(tiao)件變量的相關類。其中(zhong)有(you)兩個非(fei)常重(zhong)要的接(jie)口,wait()和notify_one(),wait()可以讓線程(cheng)陷入休(xiu)眠(mian)狀態(tai),意思就(jiu)是(shi)(shi)不(bu)干活(huo)了(le),notify_one()就(jiu)是(shi)(shi)喚醒真(zhen)正(zheng)休(xiu)眠(mian)狀態(tai)的線程(cheng),開始干活(huo)了(le)。當然還有(you)notify_all()這(zhe)個接(jie)口,顧名(ming)思義(yi),就(jiu)是(shi)(shi)通知所有(you)正(zheng)在(zai)等待的線程(cheng),起(qi)來(lai)干活(huo)了(le)。
以下是代碼的實現部分
#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
deque<int> q;
mutex mt;
condition_variable cond;
void thread_producer()
{
int count = 10;
while (count > 0)
{
unique_lock<mutex> unique(mt);
q.push_front(count);
unique.unlock();
cout << "producer a value: " << count << endl;
cond.notify_one();
this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
void thread_consumer()
{
int data = 0;
while (data != 1)
{
unique_lock<mutex> unique(mt);
while (q.empty())
cond.wait(unique);
data = q.back();
q.pop_back();
cout << "consumer a value: " << data << endl;
unique.unlock();
}
}
int main()
{
thread t1(thread_consumer);
thread t2(thread_producer);
t1.join();
t2.join();
return 0;
}
生產者:首先生產者利(li)用unique_lock來(lai)加(jia)鎖(suo),然后將(jiang)生產的數據放入隊列,打印,解鎖(suo),一旦解鎖(suo)之(zhi)后,消費者獲得了執行機會。
消費(fei)者(zhe):另一方面消費(fei)者(zhe)就會通(tong)過(guo)unique_lock獲得控制(zhi)權,也(ye)就是獲得鎖,然后判(pan)斷(duan)隊列(lie)為空的話就一直盜用(yong)wait()函(han)數阻塞在那里,等待其他(ta)線程(cheng)來喚醒(xing)它。而阻塞該(gai)線程(cheng)時,該(gai)函(han)數會自動解鎖,允許其他(ta)線程(cheng)執(zhi)行。
生產者(zhe)(zhe):再次(ci)回(hui)到生產者(zhe)(zhe)這里,生產者(zhe)(zhe)線(xian)程利用(yong)利用(yong)條件變量cond.notify_one()來通知阻塞(sai)的線(xian)程起來干活(huo)了。
消費者:阻塞在那里的(de)消費者線(xian)程一旦得到notify喚(huan)醒,該(gai)函數(shu)取(qu)消阻塞并(bing)獲取(qu)鎖(suo),然后取(qu)出隊列中(zhong)的(de)數(shu)據,并(bing)打(da)印(yin),最后解(jie)鎖(suo)。
生(sheng)產(chan)者(zhe)(zhe):再次回到生(sheng)產(chan)者(zhe)(zhe),然后(hou)生(sheng)產(chan)者(zhe)(zhe)休眠(mian)1秒(miao),這里(li)休眠(mian)是(shi)為(wei)了模擬生(sheng)產(chan)者(zhe)(zhe)生(sheng)產(chan)慢的情(qing)況,實際開發(fa)的時(shi)候不要去休眠(mian)。最后(hou)減一(yi),進入下一(yi)次生(sheng)產(chan)。
以上就(jiu)是利(li)用條件變量來實(shi)現生產消(xiao)費(fei)者模型,這個(ge)會大大降低CPU的占有率,當然代(dai)價就(jiu)是編程稍微有點麻煩(fan),但與這優化程序來比,這肯定(ding)是值的。