semaphore一篇文章搞懂信號量_第1頁
semaphore一篇文章搞懂信號量_第2頁
semaphore一篇文章搞懂信號量_第3頁
semaphore一篇文章搞懂信號量_第4頁
semaphore一篇文章搞懂信號量_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

2020-11-1616:48大小Go并發(fā)編程實(shí)戰(zhàn)16:48大小在前面的課程里,我們學(xué)習(xí)了標(biāo)準(zhǔn)庫的并發(fā)原語、原子操作和Channel,掌握了這些,你就可以解決80%的并發(fā)編程問題了。但是,如果你要想進(jìn)一步提升你的并發(fā)編程能力,就所以,在接下來的幾節(jié)課里,我會給你分享Go官方或者其他人提供的第三方庫,這節(jié)課我們先來學(xué)習(xí)信號量,信號量(Semaphore)是用來控制多個goroutine源的并發(fā)原語。信號量的概念是荷蘭計(jì)算機(jī)科學(xué)家Edsgerjstra在3不同的操作系統(tǒng)中。在系統(tǒng)中,會給每一個進(jìn)程一個信號量,代表每個進(jìn)程目前的狀態(tài)。未得到控制權(quán)的進(jìn)程,會在特定的地方被迫停下來,等待可以繼續(xù)進(jìn)行的信號到來。最簡單的信號量就是一個變量加一些并發(fā)控制的能力,這個變量是0到n之間的一個數(shù)值。當(dāng)goroutine完成對此信號量的等待(wait)時,該計(jì)數(shù)值就減1,當(dāng)goroutine完成對此信號量的釋放(release)時,該計(jì)數(shù)值就加1。當(dāng)計(jì)數(shù)值為0的時候,goroutinewait0,goroutine10本《Go1書,“僧多粥少”。所以,圖書館管理員先會讓這1萬個同學(xué)進(jìn)行登記,按照登記的順書了,圖書館管理員就會通知下一位同學(xué)來借閱這本書。這里的資源是《Go并發(fā)編程的獨(dú)家秘籍》這十本書,想讀此書的同學(xué)就是goroutine,圖書管理員就是信號量。怎么樣,現(xiàn)在是不是很好理解了?那么,接下來,我們來學(xué)習(xí)下信號量的P/V操作。P/VDijkstra在他的論文中為信號量定義了兩個操作P和V。P操作(descrease、wait、acquire)V(increase、signal、release)是增加信號11functionV(semaphoreS,integer[[S←S+functionP(semaphoreS,integerI):[ifS≥I:S←S?IS(n)n子。P操作相當(dāng)于請求資源,如果資源可用,就立即返回;如果沒有資源或者不夠,那么,它可以不斷嘗試或者阻塞等待。V操作會釋放自己持有的資源,把資源返還給信號量。信號量的值除了初始化的操作以外,只能由P/V操作改變。P1,如果新值已經(jīng)為負(fù),那么調(diào)用者會被阻塞并加入到V1,P講到這里,我想再稍微說一個題外話,我們在第2場景下,會有些goroutine始終搶不到鎖。為了處理饑餓的問題,你可以在等待隊(duì)列中做其實(shí),信號量可以分為計(jì)數(shù)信號量(countingsemaphre)和二進(jìn)位信號量(binarysemaphore)。剛剛所說的圖書館借書的例子就是一個計(jì)數(shù)信號量,它的計(jì)數(shù)可以是任意一個整數(shù)。在特殊的情況下,如果計(jì)數(shù)值只能是0或者1,那么,這個信號量就是二進(jìn)位信號量,提供了互斥的功能(要么是0,要么是),信號量來實(shí)現(xiàn)。我們一般用信號量保護(hù)一組資源,比如數(shù)據(jù)庫連接池、一組客戶端的連接、幾個打印機(jī)資源,等等。如果信號量蛻變成二進(jìn)位信號量,那么,它的P/V就和互斥鎖的一樣了。有人會很細(xì)致地區(qū)分二進(jìn)位信號量和互斥鎖。比如說,有人提出,在Windows系統(tǒng)中, Stack上也有相關(guān)的討論)。實(shí)際上,雖然在Windows系統(tǒng)中,它們的確有些區(qū)別,但是對Go語言來說,互斥鎖也可以由非持有的goroutine來釋放,所以,從行為上來說,它們并沒有嚴(yán)Go在運(yùn)行時,GogoroutinetypetypeMutexstruct{stateint32semauint324信號量的P/Vfuncfuncruntime_Semacquire(sfuncruntime_SemacquireMutex(s*uint32,lifobool,skipframesint)funcruntime_Semrelease(s*uint32,handoffbool,skipframesint)遺憾的是,它是Go運(yùn)行時內(nèi)部使用的,并沒有封裝暴露成一個對外的信號量并發(fā)原語,原則上我們沒有辦法使用。不過沒關(guān)系,Go在它的擴(kuò)展包中提供了信號量semaphore,不過這個信號量的類型名并不叫Semaphore,而是叫Weighted。之所以叫做Weighted,(初始化的資源數(shù)),其實(shí)我覺得叫SemaphoreAcquirePContext,Context超時或者cancel的機(jī)制。如果是正常獲取了資源,就返回nil;否則,就返回ctx.Err(),Release方法:相當(dāng)于V操作,可以將nTryAcquirenn回true,要么一個也不獲取,返回false。WorkerPoolCPUWorker,4slice。每個Worker一次只能處理一個整數(shù),處理完之后,才能處理下一個。11varmaxWorkers=//worker8:=9i:=rangetask//如果沒有worker可用,會阻塞在這里,直到某個workeriferr:=sema.Acquire(ctx,1);err!=nil}//啟動workergofunc(iint)defertime.Sleep(100*time.Millisecond)//task[i]=i+}iferr:=sema.Acquire(ctx,int64(maxWorkers));err!=nillog.Printf("獲取所有的worker失敗:%v", 30在這段代碼中,maingoroutine相當(dāng)于一個dispacher,負(fù)責(zé)任務(wù)的分發(fā)。它先請求信號量,如果獲取成功,就會啟動一個goroutine去處理計(jì)算,然后,這個goroutine會釋放這個信號量(有意思的是,信號量的獲取是在maingoroutine,信號量的釋放是在workergoroutine中),如果獲取不成功,就等到有信號量可以使用的時候,再去獲取。需要提醒你的是,其實(shí),在這個例子中,還有一個值得我們學(xué)習(xí)的知識點(diǎn),就是最后的那一段處理(第5行)。rr。Go擴(kuò)展庫中的信號量是使用互斥鎖+List實(shí)現(xiàn)的?;コ怄i實(shí)現(xiàn)其它字段的保護(hù),而List實(shí)現(xiàn)了一個等待隊(duì)列,等待者的通知是通過Channel的通知機(jī)制實(shí)現(xiàn)的。我們來看一下信號量Weighted2//34//56//在信號量的幾個實(shí)現(xiàn)方法里,Acquire否可用,而且還要檢測Context的Done是否已關(guān)閉。我們來看下它的實(shí)現(xiàn)代碼。func(s*Weighted)Acquire(ctxcontext.Context,nint64)error//fastpath,如果有足夠的資源,都不考慮ctx.Done的狀態(tài),將cur加上nifs.size-s.cur>=n&&s.waiters.Len()==0s.cur+=return 9ifn>s.size//依賴ctxreturn ////創(chuàng)建了一個readychan,ready:=make(chanw:=waiter{n:n,ready:elem:=//selectcase<-ctx.Done()://context的Doneerr:=selectcase<-ready://如果被喚醒了,忽略ctxerr=default:通知isFront:=s.waiters.Front()==//通知其它的waiters,ifisFront&&s.size>s.cur}}returncase<-ready://return}}fastpathacquireSlow法,以便其它Acquire被內(nèi)聯(lián)。Releasen,并喚醒等待隊(duì)列中的調(diào)用者,看是否有funcfunc(s*Weighted)Release(nint64){s.cur-=ifs.cur<0{panic("semaphore:releasedmorethan 10notifyWaiters2for3next4if==nil5//Nomorewaiters6}78ifs.size-s.cur<w.ns.cur+=w.nnotifyWaiters100個資源的時候,如果第一個等待者需要101個資源,那么,隊(duì)列中的所有等待者都會繼續(xù)等待,即使有的等待1序panic。候,也可能會出現(xiàn)死鎖現(xiàn)象,比如哲學(xué)家就餐問題。GoRelease但是,如果你傳遞一個比請求到的數(shù)量大的錯誤的數(shù)值,程序就會panic。如果傳遞一個所以,使用信號量遵循的原則就是請求多少資源,就釋放多少資源源數(shù)。Channel來實(shí)現(xiàn)。ChannelGobuffer為n的Channel很容易實(shí)現(xiàn)信號量,比如下面的代碼,我們就是使用chan用Lock和Unlock方法實(shí)現(xiàn)請求資源和釋放資源,正好實(shí)現(xiàn)了Locker接口。1//Semaphore數(shù)據(jù)結(jié)構(gòu),并且還實(shí)現(xiàn)了Locker2typesemaphorestruct34chchan5}678funcNewSemaphore(capacityint){9ifcapacity<=0capacity=1//容量為1}return&semaphore{ch:make(chan}func(s*semaphore)Lock()s.ch<-}//func(s*semaphore)Unlock() 當(dāng)然,你還可以自己擴(kuò)展一些方法,比如在請求資源的時候使用Context參數(shù)(Acquire(ctx))、實(shí)現(xiàn)TryLock量的場景,為什么官方擴(kuò)展庫的信號量的實(shí)現(xiàn)不采用這種方法呢?其實(shí),具體是什么原因,我也不知道,但是我必須要強(qiáng)調(diào)的是,官方的實(shí)現(xiàn)方式有這樣一個功能:l。除了 marusama/semaphore也實(shí)現(xiàn)了一個可以動態(tài)更改資源容量的號量,也是一個非常有特色的實(shí)現(xiàn)。如果你的資源數(shù)量并不是固定的,而是動態(tài)變化的,我建議你考慮一下這個信號量庫。這是一個很奇怪的現(xiàn)象:標(biāo)準(zhǔn)庫中實(shí)現(xiàn)基本并發(fā)原語(比如Mut)號量實(shí)現(xiàn)等待隊(duì)列和通知喚醒,但是,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論