四川網(wǎng)站建設(shè)價格為企業(yè)策劃一次網(wǎng)絡(luò)營銷活動
目錄標(biāo)題
- 什么是同步
- 生產(chǎn)者和消費者模型
- 三者之間的關(guān)系
- 消費者生產(chǎn)者模型改進(jìn)
- 生產(chǎn)者消費者模型特點
- 條件變量的作用
- 條件變量有關(guān)的函數(shù)
- 條件變量的理解
- 條件變量的使用
什么是同步
這里通過一個例子來帶著大家了解一下什么是同步,在生活中大家肯定遇到過排隊的情景比如說某個小吃店在做活動然后很多人都在排隊,然后小王恰巧路過這個小吃店,小王知道這個小吃店特別的火里面的東西買的很貴但是非常好吃,碰巧今天正在做活動并且優(yōu)惠的力度特別大,于是小王就開始來到隊隊伍的最后排起隊來準(zhǔn)備買點小吃回家,小王邊跟微信好友聊天炫耀自己搶到優(yōu)惠的同時排在小王后面的人越來越多并且小王也越來越靠近售賣的地方,過了一會終于輪到了小王,小王買了一份小吃然后付完錢準(zhǔn)備離開的時候小王突然想起來自己的媽媽也很喜歡這個小吃然后小王看到隊伍后面有好多的人不想重修排隊,于是小王又搶在下一個人上前購買之前來到收銀臺再買一份,等小王又付完錢剛離開的時候小王看到消息發(fā)現(xiàn)一號好兄弟讓他幫忙帶一份小吃于是他就又搶走下一個人來到收銀臺之前跑到收銀臺旁邊進(jìn)行挑選,等小王付完錢剛準(zhǔn)備離開的時候他又發(fā)現(xiàn)自己的二號好兄弟也找他代購小吃于是他又趕在下一個來到收銀臺之前跑到收銀臺進(jìn)行挑選購買,就這樣反反復(fù)復(fù)然后就會發(fā)現(xiàn)一個現(xiàn)象這條有很多人的隊伍停止運轉(zhuǎn)了,因為小王是離收銀臺最近的人,所以小王每次都能趕在下一個人之前來到收銀臺進(jìn)行下一次購買,這就導(dǎo)致了只有小王一個人能買到小吃搶到資源,這是不公平的也不是小吃店所期望,小吃店之所以做這么大的活動是希望通過這個活動讓更多的人都能品嘗到這份美味而不是只讓小王一個人買到這份美味,那么這就是生活中的例子,在程序的代碼中這樣的現(xiàn)象依然存在,比如說下面的代碼:
#include<iostream>
#include<string>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int ticket_num=1000;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * func(void* args)
{string name=static_cast<const char*>(args);int sum=0;while(true){pthread_mutex_lock(&mutex);// LockGuard lockguard(&mutex);if(ticket_num>0){usleep(124);cout<<name<<" 正在進(jìn)行搶票 "<< ticket_num<<endl;--ticket_num;sum++;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}cout<<name<<"搶到票的個數(shù):"<<sum<<endl;return nullptr;
}
int main()
{pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_create(&tid1,nullptr,func,(void*)"user1");pthread_create(&tid2,nullptr,func,(void*)"user2");pthread_create(&tid3,nullptr,func,(void*)"user3");pthread_join(tid1,nullptr);pthread_join(tid2,nullptr);pthread_join(tid3,nullptr);return 0;
}
這是一個搶票的程序一共有1000張票我們創(chuàng)建了3個線程互斥的欠票,如果資源的分配是公平的話我們應(yīng)該可以看到每個線程搶到票的個數(shù)應(yīng)該是相差不大的,那么將程序運行一下就可以看到下面這樣的結(jié)果:
我們發(fā)現(xiàn)user3搶到了絕大部分的票,user1只搶到了182張票,而user3卻1張票都沒有搶到,那么這就說明上面鎖資源的分配是不公平的,某些線程可以容易的申請鎖資源而有些線程卻很難申請到鎖的資源,申請到了鎖資源就要執(zhí)行一些任務(wù)完成事情,如果某個線程一直申請到了鎖的資源那么這個線程就一直處于工作狀態(tài)而一直沒有申請到鎖資源的程序就一直處于空閑狀態(tài),也就是所謂的忙的忙死閑的閑死,那為什么會出現(xiàn)這樣的現(xiàn)象呢?原因很簡單鎖只規(guī)定了互斥訪問也就是讓執(zhí)行流串行的訪問某個區(qū)域,他并沒有規(guī)定讓誰先優(yōu)先獲取鎖所以哪個線程能獲取到鎖完全由線程之間的競爭決定的,出現(xiàn)了這樣的現(xiàn)象是不符合預(yù)期的我們希望每個線程都能夠均勻的分配到工作,所以這里就采用一個方法,當(dāng)我們排隊購買東西的時一旦付完了錢要是想再次購買的話只能重新排隊來到隊伍的最后不能直接插到最前面,那么這里也是同樣的道理,我們讓線程能夠按照某種特定的順序訪問臨界資源也就是按照某種順序申請鎖資源,比如說先是A線程申請鎖再是B線程申請鎖然后是c線程申請鎖等繞了一圈之后才又是A線程申請鎖
這樣就避免了饑餓問題,每個線程都能分配到數(shù)量相當(dāng)?shù)娜蝿?wù)我們把這樣的解決方法叫做同步。
生產(chǎn)者和消費者模型
那么要想更好的了解線程的同步我們就得了解一下什么是生產(chǎn)者消費者模型,在現(xiàn)實中學(xué)生就是典型的消費者,學(xué)生一般會去商場消費買東西,那么生產(chǎn)者就是提供商品的工廠,學(xué)生不會去工廠里面買東西,而是去超市里面買東西,因為工廠不會賣東西給消費者因為機器的啟動成本比較高學(xué)生購買的量比較少,并且工廠離學(xué)生等消費群體比較遠(yuǎn)不會建立在市區(qū),所以消費者會去超市里面買東西,超市就是一個平臺他建立在市區(qū)離學(xué)生群體較近,面向消費者提供商量貨物售賣的服務(wù),面向工廠提供大量購買的請求
超市里面不同的商品就有這不同的供貨商,并且超市面向的消費者比較多,所以超市每次向供貨商進(jìn)貨的量就比較大,所以超市的作用就是集中需求分發(fā)產(chǎn)品,所以超市扮演的角色就是交易場所,生產(chǎn)者并不是無時無刻在生產(chǎn),可能在修理設(shè)備可能在停工也可能在生產(chǎn)產(chǎn)品,并且消費者也不是無時無刻在消費他們可能在工作可能在上學(xué)也可能在干任何事情,所以消費者和生產(chǎn)者之間是沒有什么關(guān)系的他們兩做的事情是不會發(fā)生干擾的,我們把這樣的現(xiàn)象稱為生產(chǎn)的過程和消費的過程解藕,那這里就有兩個問題:消費者能夠一直消費嗎?消費者不能一直消費當(dāng)把超市里面的東西買完了也就沒得買了,想消費也消費不了了所以消費者不能夠一直消費,那生產(chǎn)者能夠一直生產(chǎn)嗎?肯定也是不行的商場都貨架都擺滿了你還生產(chǎn)干嘛咧對吧賣不出去就不生產(chǎn)了對吧,所以生產(chǎn)者也不能一直生產(chǎn),超市作為臨時保存產(chǎn)品的場所他可以保存一定量的產(chǎn)品,所以他就可以保證生產(chǎn)者要生產(chǎn)的時候可以一定程度的生產(chǎn),生產(chǎn)者不想生產(chǎn)的時候我這里也有貨可以一定程度滿足消費者的需求,而消費想消費的時候超市目前的存貨量也能幾乎滿足他的需求,所以正式超市這樣的角色存在就保證了生產(chǎn)者和消費者在一定程度上的解耦,而在計算機中我們就把超市這樣的角色稱為緩沖區(qū)。這里為了讓大家更好的了解什么是解耦我們可以舉一個強耦合的例子:函數(shù)調(diào)用就是強耦合,我們以形參的形式將數(shù)據(jù)交給函數(shù)func,函數(shù)體內(nèi)對新參進(jìn)行加工將計算的結(jié)果以返回值的方式進(jìn)行返回,所以可以將函數(shù)的調(diào)用方看成生產(chǎn)者生產(chǎn)了數(shù)據(jù),把形成的變量認(rèn)為臨時的保存了數(shù)據(jù),而目標(biāo)函數(shù)就是消費者他拿著臨時數(shù)據(jù)開始了加工和消費,我們知道在main函數(shù)里面調(diào)用了func函數(shù),那在執(zhí)行func函數(shù)的時候main函數(shù)在干嘛呢?答案是什么都沒有干就在等func執(zhí)行結(jié)束,只有func執(zhí)行結(jié)束main函數(shù)才能接著執(zhí)行,所以這就是一個強耦合關(guān)系,這就好比去小孩放學(xué)回家在路上一定會買些吃邊吃邊回家,有些小孩會買辣條有些小孩會買炸火腿腸,買辣條的小孩買完就走了,而買炸火腿腸的小孩還得等,等火腿腸由生的變成熟的才能走,所以吃辣條的小孩會早些到家而買火腿腸的則會晚點到家,那么計算機中也是這樣的,解耦一定會效率會高點而耦合則會效率低點所以這就是耦合的例子。
三者之間的關(guān)系
因為生產(chǎn)者和消費者都不止是一個線程,那么接下來我們就要討論消費者和消費者之間的關(guān)系,生產(chǎn)者和生產(chǎn)者之間的關(guān)系,以及生產(chǎn)者和消費者之間的關(guān)系。生產(chǎn)者要將生產(chǎn)的東西放到超市里面,消費者要從超市里面拿東西,所以超市就是一個公共資源要被生產(chǎn)者和消費者訪問,那么這里就存在一個問題,當(dāng)超市的貨架是空的生產(chǎn)者正要往貨架上放東西而消費者這個時候正想拿東西的時候消費者能獲取物品成功嗎?答案是不確定的,理想狀態(tài)下工作人員放東西要么就是放要么就是不放沒有中間狀態(tài)的,如果有中間狀態(tài)他正在放東西的時候,他有沒有放就決定著我能不能獲取東西成功,而他有沒有放我們是不能確定的,所以這里可能就會出現(xiàn)同時訪問的問題,然后就照成了數(shù)據(jù)不一致的問題,這就好比供應(yīng)商往超市的貨架上放物品時可能存在很多步,比如說當(dāng)前放了多少物品,這些物品編碼是多少,生產(chǎn)日期是多少等等要記錄很多步驟,而這個時候消費者拿取物品就可能會導(dǎo)致信息的錯亂出現(xiàn)問題,因為超市里的某些資源是共享的所以就會出現(xiàn)資源的并發(fā)訪問的問題,所以超市就得首先被保護(hù)起來,生產(chǎn)者在往超市里面存放東西的時候消費者不能從超市里面拿東西,而消費者在從超市里面拿東西的時候生產(chǎn)者就不能往里面放東西,那么這是生活中的例子我們還可以通過計算機中的例子再進(jìn)行了解,比如說消費者線程想要從緩沖區(qū)里面讀取數(shù)據(jù)hello world但是消費者剛讀到hello的時候,生產(chǎn)者可能把之前的hello world改成hello history那這個時候就會消費者線程讀取的數(shù)據(jù)就會出現(xiàn)問題,所以生產(chǎn)者和消費者之間的關(guān)系為互斥關(guān)系,那生產(chǎn)者和生產(chǎn)者之間是什么關(guān)系呢?答案是競爭關(guān)系比如說一個貨架上面不能存放兩個牌子的物品,在超市里面一個貨架上要么是康師傅的方便要么是統(tǒng)一的防變量兩家的絕對不能放到一起,所以在計算機當(dāng)中生產(chǎn)者和生產(chǎn)者之間是互斥關(guān)系,消費者和消費者之間也是競爭關(guān)系比如說商品只剩下一份了但是兩個消費者都想要這個商品那么這個時候消費者之間就是競爭關(guān)系也就是所謂的互斥。那么看到了這里我們就可以稍微的總結(jié)一下:
- 生產(chǎn)者和生產(chǎn)者之間的關(guān)系是互斥的
- 消費者和下消費者之間的關(guān)系是互斥的
- 消費者和生產(chǎn)者之間的關(guān)系是互斥的
消費者生產(chǎn)者模型改進(jìn)
最近華為新出的mate60非常的火爆,那么我們這里就用遙遙領(lǐng)先來舉一個例子,消費者小雷想去商店買一臺遙遙領(lǐng)先用用,小雷的家離商店有點元素于是小雷花了好多時間來到商店,可是一問服務(wù)員發(fā)現(xiàn)遙遙領(lǐng)先賣完了沒貨了于是小雷只能失望的回家,第二天小雷又花了很多時間跑到商店詢問瑤瑤領(lǐng)先有沒有貨,可是這個手機實在是太火爆了導(dǎo)致現(xiàn)在依然是缺貨的狀態(tài),所以小王又只能這么無功而返,就這樣第三天第四天第五天,小雷天天來找服務(wù)員購買mate60,但是服務(wù)員只能一次又一次的告訴小雷這個手機缺貨了請明天再來看看,所以這樣的行為
既浪費了消費者的時間也浪費了超市服務(wù)員的經(jīng)歷,同樣的道理生產(chǎn)者生產(chǎn)商品的時候也會不停的詢問超市是否需要該物品,如果一段時間不需要的話也勢必會導(dǎo)致生產(chǎn)者不停的詢問,這樣既浪費了生產(chǎn)者的精力也浪費了商店服務(wù)員的精力,那么為了解決上述的問題商店的服務(wù)員可以和消費者生產(chǎn)者之間使用微信聯(lián)系,當(dāng)消費者想知道物品是否有貨的時候就可以使用微信進(jìn)行聯(lián)系不需要親自來到商店,生產(chǎn)者想知道是否需要補貨的時候也可以使用微信進(jìn)行聯(lián)系而不需要親自來到商店,所以這樣就保證了生產(chǎn)者生產(chǎn)往商店里放了一部分,消費者就從商店里面拿走一部分,這樣就讓生產(chǎn)者和消費者之間協(xié)同了起來不僅提供了生產(chǎn)者的效率還提供了消費者的效率,生產(chǎn)者生產(chǎn)數(shù)據(jù)的時候會進(jìn)行枷鎖和解鎖緩沖區(qū),當(dāng)緩沖區(qū)滿了之后生產(chǎn)者又會枷鎖和解鎖的訪問緩沖區(qū)能否裝的下資源,如果生產(chǎn)者不停的循環(huán)枷鎖解鎖這就又會導(dǎo)致消費者的饑餓問題,反過來也是同樣的道理,所以生產(chǎn)者和消費者之間的關(guān)系不僅存在著互斥的關(guān)系還存在著同步的關(guān)系,所以這里總結(jié)一下就是321原則:
3表示:
3種關(guān)系(消費者和消費者的關(guān)系(互斥), 生產(chǎn)者和生產(chǎn)者的關(guān)系(互斥),消費者和生產(chǎn)者的關(guān)系(互斥(保證共享資源的安全性),同步(保證生產(chǎn)者和消費的工作效率不然其中的其中一方處于饑餓狀態(tài)不讓其中一方干太多的無用功:詢問是否有資源或者詢問是否還能裝的下資源))。
2表示
2種角色也就是生產(chǎn)者線程和消費者線程。
1表示
1個交易場所也就是一段特定結(jié)構(gòu)的緩沖區(qū)。
那么只要我們想實現(xiàn)生產(chǎn)消費模型,本質(zhì)上就是要維護(hù)好上面的321原則。
生產(chǎn)者消費者模型特點
- 生產(chǎn)線程和消費線程進(jìn)行解耦。
- 解決生產(chǎn)和消費一段時間的忙閑不均的問題,比如說過年的時候工廠的工人都回家所以工廠的生產(chǎn)效率就非常的低,但是人們又要買很多的東西回家拜年,那么這個時候工廠的生產(chǎn)效率和消費者的消費效率就是不對等的,如果沒有商店就會導(dǎo)致很多的人想買卻買不著商品的問題,但是因為超市的存在他可以預(yù)先的存儲一部分的商品,在工廠的生產(chǎn)效率特別低的情況的下也能滿足消費者特別高的需求,所以這就解決了生產(chǎn)和消費的忙線不均的問題。
- 提高效率,比如說超市的存在提供了消費者和生產(chǎn)者的效率,如果沒有超市消費者就只能自己前往遙遠(yuǎn)的工廠進(jìn)行購買,而生產(chǎn)者也不敢大量的生產(chǎn)的物品怕消費者嫌棄太遠(yuǎn)不敢來了,那么在程序里面也是同樣的道理:我們知道m(xù)ain函數(shù)在調(diào)用其他函數(shù)的時候是無法繼續(xù)執(zhí)行的,而函數(shù)可能會因為用戶輸入數(shù)據(jù)較慢的原因?qū)е耺ain線程一直處于等待狀態(tài),但是有了生產(chǎn)者和消費者模型就可以把main線程中用戶輸入數(shù)據(jù)的過程看成生產(chǎn)者,調(diào)用func函數(shù)執(zhí)行func函數(shù)打印數(shù)據(jù)當(dāng)成消費者,這樣用戶就可以往緩沖區(qū)中輸入大量的數(shù)據(jù)即使某一刻不輸入數(shù)據(jù)也不會導(dǎo)致func執(zhí)行的情況,因為緩沖區(qū)中還存在數(shù)據(jù)可以繼續(xù)供func函數(shù)執(zhí)行這樣就提高了程序的運行效率。那么這就是生產(chǎn)者消費者模型的特點。但是這個高效并不是絕對的比如說消費想要獲取數(shù)據(jù)但是超市里面什么數(shù)據(jù)都沒有這時就沒有辦法獲取,同理生產(chǎn)者在生產(chǎn)數(shù)據(jù)的時候發(fā)現(xiàn)消費者正在拿數(shù)據(jù),這時即使將數(shù)據(jù)生產(chǎn)出來了也沒有辦法將數(shù)據(jù)放進(jìn)去,所以在一些場景下該模型就會演化成生產(chǎn)者生產(chǎn)一個消費者消費一個,那這個高效究竟高效在哪里呢?
條件變量的作用
在上面的學(xué)習(xí)中我們知道當(dāng)超市里面的資源滿了或者空了的時候,消費者和生產(chǎn)者可能就會不停的枷鎖訪問是否需要資源或者是否有資源然后再解鎖,雖然這樣保證了超市這個公共數(shù)據(jù)的安全,但是這樣的現(xiàn)象不僅浪費了消費者的資源還浪費了生產(chǎn)者的資源,所以我們就讓消費者和生產(chǎn)者之間為同步關(guān)系,那么為了實現(xiàn)這個同步的關(guān)系我們就得使用條件變量,當(dāng)一個線程互斥地訪問某個變量時,它可能發(fā)現(xiàn)在其它線程改變狀態(tài)之前,它什么也做不了。例如一個線程訪問隊列時,發(fā)現(xiàn)隊列為空,它只能等待,直到其它線程將一個節(jié)點添加到隊列中時他才能獲取到該節(jié)點,那么這種情況就需要用到條件變量。條件變量的作用就是當(dāng)發(fā)現(xiàn)公共數(shù)據(jù)中沒有數(shù)據(jù)時就讓該線程掛起等待,等公共資源中有數(shù)據(jù)的時候再將等待的線程喚醒即可。
條件變量有關(guān)的函數(shù)
條件變量也是一個數(shù)據(jù)類型,使用條件變量之前得定義一個pthread_cond_t類型的對象,定義了該變量之后就可以使用pthread_cond_init來進(jìn)行初始化,與鎖相似如果條件變量是全局的話也可以使用PTHREAD_COND_INITIALIZER
來初始化,該函數(shù)的聲明如下:
第一個參數(shù)就要傳遞一個條件變量的指針,第二個參數(shù)表示條件變量的屬性這里不管直接傳遞null就可以,既然有條件變量的初始化那么就有條件變量的銷毀,銷毀就要用pthread_cond_destroy函數(shù),想要銷毀哪個變量就只需要傳遞對應(yīng)變量的地址即可。生產(chǎn)者在訪問資源的時候都是先枷鎖然后判斷生產(chǎn)的條件是否滿足最后解鎖,這就會導(dǎo)致其他線程的饑餓問題,但是有了條件變量之后就可以做到條件不滿足的時候就不再申請鎖資源了而是將自己掛起,那么這里的掛起用到的就是pthread_cond_wait函數(shù)該函數(shù)的聲明如下,該函數(shù)的參數(shù)我們后面再聊:
既然有函數(shù)能在資源條件不滿足的時候?qū)⒕€程掛起,那么當(dāng)資源條件滿足的時候一定存在一個函數(shù)可以將之前等待的線程喚醒那么這個函數(shù)就是pthread_cond_signal。
條件變量的理解
1.一個例子
公司招人的時候是需要面試的,并且每個崗位都會有大量的人來進(jìn)行應(yīng)聘,所以面試官所在的面試間外面就圍著大量的人,每當(dāng)面試完一個人的時候就讓離房間門最近的一個人進(jìn)來面試,但是這樣的做法導(dǎo)致了面試的環(huán)境非常的亂,很多求職人員會因為誰靠的最近而吵來吵去爭來爭去,這既影響了面試時的環(huán)境也影響了面試的效率
所以該公司就在離面試房間較遠(yuǎn)處插上了一個牌子,然后規(guī)定所有前來面試的人不要在面試房間周圍等待,而是在這個牌子的左邊按照先來后到的順序排成一排進(jìn)行等待:
這樣每面試完一個人就可以直接讓牌左邊的第一個人前往面試房間即可不需要進(jìn)行挑選了,如果這個時候有人前來準(zhǔn)備面試只用排到隊伍的最左邊即可,那么我們就把這里的牌子就稱之為條件變量,所有應(yīng)聘者等待面試的時候只能去條件牌子下面等待,而面試官叫人前來面試的時候也只會從牌子里面叫應(yīng)聘者,那么這是現(xiàn)實生活在操作系統(tǒng)當(dāng)中,如果共享資源的條件不滿足的話,線程就只能去定義好的條件變量的上面進(jìn)行等待,當(dāng)共享資源的條件滿足時便會去條件變量上面喚醒線程。
2.一張圖
條件變量就是一個結(jié)構(gòu)體變量,里面有個status表示當(dāng)前條件變量的狀態(tài),還有一個task_struct指針用來維護(hù)的隊列
操作系統(tǒng)中存在多個線程這些線程在操作系統(tǒng)內(nèi)部都是一個個的task_struct,所以當(dāng)一個線程申請到了鎖但是發(fā)現(xiàn)當(dāng)前的資源不就緒的時候就可以將當(dāng)前進(jìn)程的task_struct鏈接到struct_cond中的task_struct隊列里面
等未來我們發(fā)現(xiàn)共享資源的條件滿足的時候就可以調(diào)用pthread_cond_signal函數(shù),該函數(shù)就是將線程的pcb從隊列中取出來放到cpu中執(zhí)行,那么這就是條件變量的基本結(jié)構(gòu)。
條件變量的使用
那么這里我們就通過修改文章一開始的例子來帶著大家了解一下條件變量有關(guān)函數(shù)的使用,首先原來代碼的大致框架是這樣:
#include<iostream>
#include<string>
#include<unistd.h>
#include<pthread.h>
using namespace std;
int ticket_num=1002;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void * func(void* args)
{
}
int main()
{pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_create(&tid1,nullptr,func,(void*)"user1");pthread_create(&tid2,nullptr,func,(void*)"user2");pthread_create(&tid3,nullptr,func,(void*)"user3");pthread_join(tid1,nullptr);pthread_join(tid2,nullptr);pthread_join(tid3,nullptr);return 0;
}
我們就可以創(chuàng)建一個全局的條件變量
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
然后在func函數(shù)里面我們依然是先轉(zhuǎn)換獲取線程的名字,然后創(chuàng)建一個變量表示當(dāng)前線程獲得的票數(shù)總數(shù),然后我們創(chuàng)建while循環(huán)表示不斷的進(jìn)行搶票,while循環(huán)的開始我們就先申請鎖,一旦鎖申請成功了我們就直接將該線程放到條件變量里面掛起,pthread_wait函數(shù)的聲明如下:
第一個參數(shù)表示在哪個條件變量下面等待,第二個參數(shù)表示當(dāng)前線程所持有的鎖所以第二個參數(shù)我們傳遞鎖的地址(至于為什么要傳遞鎖的地址我們后面再聊),那么這里的代碼如下:
void * func(void* args)
{string name=static_cast<const char*>(args);int sum=0;while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);}
}
然后我們就判斷一下如果當(dāng)前的票數(shù)大于0就對ticket減減對sum的值++并打印一句話,否則就直接使用break跳出循環(huán),不管是哪種情況都得將鎖的資源釋放,循環(huán)結(jié)束后就打印一下當(dāng)前線程搶到票數(shù)的總和:
void * func(void* args)
{string name=static_cast<const char*>(args);int sum=0;while(true){pthread_mutex_lock(&mutex);pthread_cond_wait(&cond,&mutex);if(ticket_num>0){cout<<name<<" 正在進(jìn)行搶票 "<< ticket_num<<endl;--ticket_num;sum++;pthread_mutex_unlock(&mutex);}else{pthread_mutex_unlock(&mutex);break;}}cout<<name<<"搶到票的個數(shù):"<<sum<<endl;return nullptr;
}
因為我們在線程獲取鎖的下一步就將其添加到了條件變量里面他們沒有辦法自己將自己喚醒,所以就得在主線程里面調(diào)用pthread_cond_signal函數(shù)將條件變量中的線程一個一個的喚醒,該函數(shù)的聲明如下:
參數(shù)表示你要喚醒哪個條件變量的線程,那么這里的代碼就如下:
int main()
{pthread_t tid1;pthread_t tid2;pthread_t tid3;pthread_create(&tid1,nullptr,func,(void*)"user1");pthread_create(&tid2,nullptr,func,(void*)"user2");pthread_create(&tid3,nullptr,func,(void*)"user3");for(int i=0;i<1002+3;i++){usleep(1234);pthread_cond_signal(&cond);}pthread_join(tid1,nullptr);pthread_join(tid2,nullptr);pthread_join(tid3,nullptr);return 0;
}
因為這里的3個線程在ticket為0的時候也會申請鎖然后去條件變量下等待,所以這里得多加三次那么這里我們的代碼就修改完了,程序運行的結(jié)果如下:
可以看到這里的運行結(jié)果符合我們的預(yù)期,那么這就是信號量的作用和用法,在上面的圖片中我們還遇到一個叫
pthread_cond_broadcast函數(shù)這函數(shù)的作用也是喚醒線程,pthread_cond_signal函數(shù)是一次喚醒一個線程,那么這個函數(shù)的作用就是一次喚醒所有等待的線程,比如說將代碼修改成下面這樣:
for(int i=0;i<1002+3;i++)
{sleep(1);pthread_cond_broadcast(&cond);cout<<"----------------------"<<endl;}
運行的結(jié)果如下:
可以看到一次性喚醒了三個線程那么這就是信號量的使用,最后我們來談?wù)劄槭裁葱盘柫繏炱鸷瘮?shù)得傳遞一個鎖指針,原因很簡單你被掛起了其他線程還得申請這個鎖對公共資源進(jìn)行訪問,所以得傳遞這個鎖指針來釋放你之前申請的鎖資源供其他線程使用,等你被喚醒的時候又會將你之前申請的鎖資源還給你,那么這就是本篇文章的全部內(nèi)容。