熱線電話:0755-23712116
郵箱:contact@shuangyi-tech.com
地址:深圳市寶安區(qū)沙井街道后亭茅洲山工業(yè)園工業(yè)大廈全至科技創(chuàng)新園科創(chuàng)大廈2層2A
信號(hào)量用來干嘛的呢?搜尋答案的話,很多人都會(huì)告訴你主要用于線程同步的,意思就是線程通信的。簡單來說,比如我運(yùn)行了2個(gè)線程A和B,但是我希望B線程在A線程之前執(zhí)行,那么我們就可以用信號(hào)量來處理。有些人可能會(huì)疑惑,那么麻煩干嘛?你不是要B線程先執(zhí)行嗎?那么我讓A線程休眠一點(diǎn)時(shí)間不就可以了嗎?沒錯(cuò),這個(gè)思路是可以的,但是如果B線程也因?yàn)槟承┰?比如硬件,操作系統(tǒng)的原因)導(dǎo)致延緩執(zhí)行了,這該怎么辦?到底A線程該休眠多少時(shí)間合適呢?所以正確的做法就是在B線程阻塞,A線程去喚醒這個(gè)阻塞線程。
看到這兒,看過我前面文章的朋友可能一眼就看出來了這個(gè)不就是前面講的生產(chǎn)消費(fèi)者模型提到的用法嗎?
沒錯(cuò),信號(hào)量的實(shí)現(xiàn)也是靠條件變量和互斥鎖。
所以雖然C++中并沒有在語言級(jí)別上支持信號(hào)量,但同樣的我們可以利用以上兩個(gè)來自己實(shí)現(xiàn)一個(gè)。
這里我也不得不提一句,條件變量和互斥鎖組合使用真的非常強(qiáng)大,生產(chǎn)消費(fèi)者模型中用到了,線程池中用到了,現(xiàn)在說的信號(hào)量也用到了,所以大家一定要好好掌握條件變量和互斥鎖的使用,它們倆是你在多線程世界中縱橫捭闔的利劍。
那么我們?nèi)绾斡肅++來實(shí)現(xiàn)一個(gè)信號(hào)量呢?
#ifndef _SEMAPHORE_H
#define _SEMAPHORE_H
#include <mutex>
#include <condition_variable>
using namespace std;
class Semaphore
{
public:
Semaphore(long count = 0) : count(count) {}
//V操作,喚醒
void signal()
{
unique_lock<mutex> unique(mt);
++count;
if (count <= 0)
cond.notify_one();
}
//P操作,阻塞
void wait()
{
unique_lock<mutex> unique(mt);
--count;
if (count < 0)
cond.wait(unique);
}
private:
mutex mt;
condition_variable cond;
long count;
};
#endif
信號(hào)量里面用到了一個(gè)叫PV操作的東西,P操作時(shí)阻塞,一般用wait()函數(shù),V操作是喚醒,一般用singal()函數(shù),至于不叫WS操作,反而為什么叫PV操作呢?網(wǎng)上說是因?yàn)樘岢鲞@一系統(tǒng)方法的人狄克斯特拉用荷蘭文定義的,因?yàn)樵诤商m文中,通過叫passeren,釋放叫vrijgeven,PV操作因此得名。對(duì)我們來說,這些也沒有太大的意義,記住這些定義就好了,畢竟定義這種東西,是不以我們的意志為轉(zhuǎn)移的。
寫好了信號(hào)量的接口,那我們?nèi)绾问褂眠@個(gè)信號(hào)量呢?這個(gè)就需要我們?cè)谕獠繉懸粋€(gè)多線程的調(diào)用函數(shù)來調(diào)用。
#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();
}
這里我們想讓funB線程運(yùn)行,然后再運(yùn)行funA,多線程是通過時(shí)間片輪詢來執(zhí)行的。
假設(shè)先開始跑funA,執(zhí)行到sem.wait()的時(shí)候,進(jìn)入wait函數(shù)可知,count減1,小于0,會(huì)發(fā)生阻塞,等待其他線程喚醒。
然后就會(huì)切換到funB,這里即使休眠了1秒也不會(huì)切換到funA,因?yàn)槟沁呑枞?,沒有其他線程喚醒的話就會(huì)一直阻塞。funB休眠完之后,就會(huì)打印出結(jié)果,然后執(zhí)行sem.signal(),進(jìn)入signal函數(shù)可知,count加1,小于等于0,會(huì)喚醒其他阻塞的線程。
然后再切到funA,執(zhí)行后面的操作,打印出結(jié)果。
所以這個(gè)就一定能保證funB先執(zhí)行,funA后執(zhí)行。當(dāng)然前提是初始化信號(hào)量對(duì)象的時(shí)候,要初始化為0。
Semaphore sem(0);
信號(hào)量用在多線程多任務(wù)同步的,一個(gè)線程完成了某一個(gè)動(dòng)作就通過信號(hào)量告訴別的線程,別的線程再進(jìn)行某些動(dòng)作。像這里funB完成任務(wù)之后就通過信號(hào)量的PV操作告訴funA線程可以開始任務(wù)了。
最后需要注意的是,信號(hào)量不僅可以用于進(jìn)程也可用于線程,它比條件變量要復(fù)雜很多,條件變量僅限于線程內(nèi)使用,至于進(jìn)程間如何使用信號(hào)量通信,后期我們?cè)谟懻摗?/p>