熱線電話(hua):0755-23712116
郵箱:contact@legoupos.cn
地址:深圳(zhen)市寶安區沙井街道(dao)后亭(ting)茅(mao)洲(zhou)山工(gong)業園工(gong)業大廈全至科技創新園科創大廈2層2A
信(xin)(xin)號(hao)量用(yong)來(lai)干嘛的(de)呢?搜(sou)尋答(da)案的(de)話,很多人都會(hui)(hui)告訴你主要(yao)用(yong)于線(xian)(xian)程(cheng)(cheng)同步(bu)的(de),意思(si)就(jiu)(jiu)(jiu)是(shi)(shi)線(xian)(xian)程(cheng)(cheng)通信(xin)(xin)的(de)。簡單來(lai)說,比如我運行(xing)(xing)(xing)了2個(ge)線(xian)(xian)程(cheng)(cheng)A和B,但(dan)是(shi)(shi)我希望(wang)B線(xian)(xian)程(cheng)(cheng)在A線(xian)(xian)程(cheng)(cheng)之前執行(xing)(xing)(xing),那(nei)么(me)我們就(jiu)(jiu)(jiu)可(ke)以用(yong)信(xin)(xin)號(hao)量來(lai)處理。有(you)些人可(ke)能(neng)會(hui)(hui)疑惑,那(nei)么(me)麻煩干嘛?你不是(shi)(shi)要(yao)B線(xian)(xian)程(cheng)(cheng)先執行(xing)(xing)(xing)嗎(ma)?那(nei)么(me)我讓(rang)A線(xian)(xian)程(cheng)(cheng)休(xiu)(xiu)眠一點時間不就(jiu)(jiu)(jiu)可(ke)以了嗎(ma)?沒(mei)錯,這(zhe)個(ge)思(si)路是(shi)(shi)可(ke)以的(de),但(dan)是(shi)(shi)如果B線(xian)(xian)程(cheng)(cheng)也因(yin)為某些原因(yin)(比如硬件,操(cao)作系(xi)統的(de)原因(yin))導致延緩(huan)執行(xing)(xing)(xing)了,這(zhe)該怎么(me)辦(ban)?到底(di)A線(xian)(xian)程(cheng)(cheng)該休(xiu)(xiu)眠多少時間合適(shi)呢?所以正確的(de)做法就(jiu)(jiu)(jiu)是(shi)(shi)在B線(xian)(xian)程(cheng)(cheng)阻(zu)塞(sai),A線(xian)(xian)程(cheng)(cheng)去(qu)喚醒這(zhe)個(ge)阻(zu)塞(sai)線(xian)(xian)程(cheng)(cheng)。
看到(dao)這兒,看過我前(qian)面文(wen)章的朋友可能(neng)一眼就看出來了這個不就是前(qian)面講的生(sheng)產(chan)消費者模型提到(dao)的用法(fa)嗎?
沒錯,信號(hao)量(liang)的實(shi)現(xian)也是靠條件變量(liang)和互斥鎖。
所以(yi)雖然C++中并沒(mei)有在(zai)語言(yan)級別上支持信號量,但同樣的我們可以(yi)利用以(yi)上兩(liang)個來自(zi)己實現一個。
這里我也不得(de)不提一句,條件變量(liang)和(he)(he)互斥(chi)鎖(suo)組(zu)合(he)使用真的非常強大,生(sheng)產消費者(zhe)模型中(zhong)用到了,線(xian)程池中(zhong)用到了,現在說的信號量(liang)也用到了,所以大家(jia)一定要(yao)好好掌握條件變量(liang)和(he)(he)互斥(chi)鎖(suo)的使用,它們倆是(shi)你在多線(xian)程世界中(zhong)縱橫捭(bai)闔的利劍(jian)。
那么我們如何用C++來實(shi)現一個(ge)信號(hao)量呢?
#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H
#include <mutex>
#include <condition_variable>
using namespace std;
class Semaphore
{
public:
Semaphore(long count = 0) : count(count) {}
//V操(cao)作,喚醒(xing)
void signal()
{
unique_lock<mutex> unique(mt);
++count;
if (count <= 0)
cond.notify_one();
}
//P操作,阻(zu)塞
void wait()
{
unique_lock<mutex> unique(mt);
--count;
if (count < 0)
cond.wait(unique);
}
private:
mutex mt;
condition_variable cond;
long count;
};
#endif
信號量里面用(yong)到了一個叫(jiao)(jiao)PV操作(zuo)(zuo)(zuo)的東西(xi),P操作(zuo)(zuo)(zuo)時阻(zu)塞(sai),一般用(yong)wait()函(han)數,V操作(zuo)(zuo)(zuo)是(shi)喚醒(xing),一般用(yong)singal()函(han)數,至于(yu)不(bu)叫(jiao)(jiao)WS操作(zuo)(zuo)(zuo),反而為(wei)什么叫(jiao)(jiao)PV操作(zuo)(zuo)(zuo)呢?網上(shang)說是(shi)因(yin)(yin)為(wei)提出這一系統(tong)方法的人(ren)狄克(ke)斯(si)特拉用(yong)荷蘭文定義(yi)的,因(yin)(yin)為(wei)在荷蘭文中,通過叫(jiao)(jiao)passeren,釋放(fang)叫(jiao)(jiao)vrijgeven,PV操作(zuo)(zuo)(zuo)因(yin)(yin)此得名。對我們來說,這些也(ye)沒(mei)有(you)太大的意義(yi),記住這些定義(yi)就(jiu)好(hao)了,畢(bi)竟定義(yi)這種東西(xi),是(shi)不(bu)以我們的意志為(wei)轉移的。
寫好(hao)了(le)信號量的(de)接口,那我們如何使(shi)用(yong)這(zhe)個(ge)信號量呢(ni)?這(zhe)個(ge)就(jiu)需要我們在外(wai)部寫一個(ge)多線程(cheng)的(de)調用(yong)函數(shu)來調用(yong)。
#include "semaphore.h"
#include <thread>
#include <iostream>
using namespace std;
Semaphore sem(0);
void funA()
{
sem.wait();
//do something
cout << "funA" << endl;
}
void funB()
{
this_thread::sleep_for(chrono::seconds(1));
//do something
cout << "funB" << endl;
sem.signal();
}
int main()
{
thread t1(funA);
thread t2(funB);
t1.join();
t2.join();
}
這里(li)我們想(xiang)讓(rang)funB線程運行,然后(hou)再運行funA,多(duo)線程是通過時間片(pian)輪(lun)詢來執行的。
假(jia)設先(xian)開始跑funA,執行(xing)到(dao)sem.wait()的時候,進入wait函數可知,count減1,小(xiao)于0,會發生阻塞,等(deng)待其他(ta)線程喚醒。
然后就會(hui)(hui)(hui)切換到(dao)funB,這(zhe)里即使休眠了1秒也不(bu)會(hui)(hui)(hui)切換到(dao)funA,因(yin)為那邊阻塞(sai)了,沒有其他線程喚(huan)醒的話(hua)就會(hui)(hui)(hui)一直阻塞(sai)。funB休眠完之后,就會(hui)(hui)(hui)打印出結果,然后執行sem.signal(),進入signal函(han)數可知,count加1,小于等于0,會(hui)(hui)(hui)喚(huan)醒其他阻塞(sai)的線程。
然后(hou)再切到funA,執行后(hou)面的操(cao)作,打印出結果。
所(suo)以(yi)這個就(jiu)一定(ding)能保證funB先執(zhi)行,funA后(hou)執(zhi)行。當然前(qian)提(ti)是初始化信號量(liang)對象的時(shi)候,要初始化為0。
Semaphore sem(0);
信(xin)號(hao)量(liang)用(yong)在多(duo)線程多(duo)任(ren)務(wu)同步的(de)(de),一個(ge)線程完成了(le)某一個(ge)動(dong)作(zuo)(zuo)就(jiu)通過信(xin)號(hao)量(liang)告(gao)訴別的(de)(de)線程,別的(de)(de)線程再進行某些動(dong)作(zuo)(zuo)。像這里funB完成任(ren)務(wu)之后就(jiu)通過信(xin)號(hao)量(liang)的(de)(de)PV操作(zuo)(zuo)告(gao)訴funA線程可(ke)以(yi)開始任(ren)務(wu)了(le)。
最后(hou)需要注意(yi)的(de)是,信號量不僅可以(yi)用于進(jin)程也可用于線(xian)程,它比條件變量要復雜很多,條件變量僅限于線(xian)程內使用,至于進(jin)程間如何使用信號量通(tong)信,后(hou)期我們在討論。