GO并發(fā)編程實(shí)戰(zhàn)第二版,這本書與第1版差別是因?yàn)檎姹緯莆樟藭械拇蟛糠謨?nèi)容而秒殺了幾個(gè)甚至十幾個(gè)Go程序員的職位,從章節(jié)結(jié)構(gòu)和內(nèi)容都有大幅調(diào)整。
這本書與第1版最大的差別有3個(gè)
1.緊跟Go的1.8版本。
在這個(gè)行當(dāng)?shù)母魑欢紤?yīng)該知道,學(xué)技術(shù)就要學(xué)最新的技術(shù)。即使暫時(shí)用不上,也要在思維和思路上與技術(shù)前沿同步。更何況對(duì)于Go語言來說,版本間的向后兼容做得如此之好,我們更有理由跟上最新版本,享受語言本身帶來的紅利(更豐富的庫、更高的性能,等等)。
2.章節(jié)結(jié)構(gòu)和內(nèi)容都有大幅調(diào)整。
為了更合理、更科學(xué)地為大家呈現(xiàn)Go語言的獨(dú)特魅力和內(nèi)在奧妙,我和編輯們共同確定了新的大綱和結(jié)構(gòu)。在第2版里,基礎(chǔ)編程講得少了(更易速查),并發(fā)編程講得更多了(更加深入和細(xì)致)。
3.示例代碼得到全面且徹底的修訂。
第1版的示例代碼無論從編排、設(shè)計(jì)和實(shí)現(xiàn)水準(zhǔn)上都已經(jīng)落后了,且無法體現(xiàn)Go1.8的最新變化。在本次改版中,我完全改變了代碼包的編排方式,可以讓大家快速地找到每章每節(jié)的實(shí)例。同時(shí),我?guī)缀鯇?duì)所有中大型的示例都進(jìn)行了改造,也幾乎改進(jìn)了所有示例代碼文件。
目錄
GO并發(fā)編程實(shí)戰(zhàn)簡介
現(xiàn)在,讓我們?cè)俅尉劢沟絪ync代碼包。除了我們介紹過的互斥鎖、讀寫鎖和條件變量,該代碼包還為我們提供了幾個(gè)非常有用的API。其中一個(gè)比較有特色的就是結(jié)構(gòu)體類型sync.Once和它的Do方法。
與代表鎖的結(jié)構(gòu)體類型sync.Mutex和sync.RWMutex一樣,sync.Once也是開箱即用的。換句話說,我們僅需對(duì)它進(jìn)行簡單的聲明即可使用,就像這樣:
如上所示,我們聲明了一個(gè)名為once的sync.Once類型的變量之后,立刻就可以調(diào)用它的指針方法Do了。
該類型的方法Do可以接受一個(gè)無參數(shù)、無結(jié)果的函數(shù)值作為其參數(shù)。該方法一旦被調(diào)用,就會(huì)調(diào)用被作為參數(shù)傳入的那個(gè)函數(shù)。從這一點(diǎn)看,該方法的功能實(shí)在是稀松平常。不過,重點(diǎn)并不在這里。
我們對(duì)一個(gè)sync.Once類型值的指針方法Do的有效調(diào)用次數(shù)永遠(yuǎn)會(huì)是1。也就是說,無論我們調(diào)用這個(gè)方法多少次,也無論我們?cè)诙啻握{(diào)用時(shí)傳遞給它的參數(shù)值是否相同,都僅有第一次調(diào)用是有效的。無論怎樣,只有我們第一次調(diào)用該方法時(shí)傳遞給它的那個(gè)函數(shù)會(huì)被執(zhí)行。請(qǐng)看下面的示例:
在onceDo函數(shù)中,我們利用for語句連續(xù)三次異步的調(diào)用once變量的Do方法。這三次調(diào)用傳給Do方法的參數(shù)值都是相同的,都是變量fi所代表的匿名函數(shù)值。這個(gè)函數(shù)值的功能是先改變num變量的值再向非緩沖的sign通道發(fā)送一個(gè)true。變量num的值可以表示出once的Do方法被有效調(diào)用的次數(shù),而通道sign則被用來傳遞代表了fi函數(shù)被執(zhí)行完畢的信號(hào)。請(qǐng)注意,為了能夠精確的表達(dá)出fi函數(shù)是在哪一次(或哪幾次)調(diào)用once.Do方法的時(shí)候被執(zhí)行的,我們?cè)谶@里使用了閉包。在每次迭代之初,我們賦給fi變量的函數(shù)值都是對(duì)變量f所代表的函數(shù)值進(jìn)行閉包的一個(gè)結(jié)果值。我們使用變量ii作為f函數(shù)中的自由變量,并在閉包的過程中把for代碼塊中的變量i的值加1后再與該自由變量綁定在一起。這樣就生成了為當(dāng)次迭代專門定制的函數(shù)fi。每次迭代中生成的fi函數(shù)在被執(zhí)行的時(shí)候都會(huì)修改變量num的值。這些新的值不會(huì)出現(xiàn)重復(fù),并且非常有助于我們倒推出所有的曾賦給自由變量的ii的值。這樣,我們就可以知道哪個(gè)(或哪些)fi函數(shù)被真正的執(zhí)行了。
函數(shù)onceDo中的第二條for語句的作用是等待之前的那三個(gè)異步調(diào)用的完成。讀者可能已經(jīng)發(fā)現(xiàn),這兩條for語句的預(yù)設(shè)迭代次數(shù)是一致的。在第二條for語句中,我們使用了select語句,并且為針對(duì)sign通道的接收操作設(shè)定了超時(shí)時(shí)間(100毫秒)。這是為了當(dāng)永遠(yuǎn)無法從sign通道中接收元素值的時(shí)候不至于造成永久的阻塞。select語句中的每個(gè)case在被執(zhí)行時(shí)都會(huì)打印出相應(yīng)的內(nèi)容。這有助于我們觀察程序的實(shí)際運(yùn)行情況。最后,我們還會(huì)打印出num變量的值。據(jù)此,我們可以判斷在前面幾次傳遞給Do方法的fi是否都被執(zhí)行了。
在執(zhí)行onceDo函數(shù)之后,我們會(huì)看到如下打印內(nèi)容:
上面的打印內(nèi)容表明,在成功從sign通道接收了一個(gè)元素值之后,出現(xiàn)了兩次接收操作超時(shí)的情況。我們不用考慮在對(duì)sign通道的接收操作開始之時(shí)匿名函數(shù)fi還沒有被執(zhí)行完畢的情況。因?yàn)?00毫秒的時(shí)間已經(jīng)足夠執(zhí)行它很多很多次的了。因此,這兩次接收操作超時(shí)應(yīng)該是當(dāng)時(shí)沒有正在為此等待的對(duì)sign通道的發(fā)送操作導(dǎo)致的(注意,sign是一個(gè)非緩沖通道)。綜上所述,我們可以初步判斷,傳遞給once.Do方法的匿名函數(shù)fi只被執(zhí)行了一次。并且,這僅有一次的執(zhí)行的對(duì)象是在我們第一次調(diào)用該方法時(shí)傳遞給它的那個(gè)fi函數(shù)。
依據(jù)最后一行打印內(nèi)容,我們可以證實(shí)上述判斷。num變量的值為2意味著它只被修改了一次,并且是在自由變量ii為1的時(shí)候被修改的。這就可以證實(shí),只有在for循環(huán)的第一次迭代時(shí)傳遞給once.Do方法的那個(gè)fi函數(shù)被執(zhí)行了。這也符合sync.Once類型及其指針方法Do的語義。
請(qǐng)注意,這個(gè)僅被執(zhí)行一次的限制只是針對(duì)單個(gè)sync.Once類型值來說的。換句話說,每個(gè)sync.Once類型值的指針方法Do都可以被有效的調(diào)用一次。
這個(gè)sync.Once類型的典型應(yīng)用場景就是執(zhí)行僅需執(zhí)行一次的任務(wù)。例如,數(shù)據(jù)庫連接池的初始化任務(wù)。又例如,一些心跳檢測之類的實(shí)時(shí)監(jiān)測任務(wù)。等等。
在一探sync.Once類型及其指針方法Do的內(nèi)部實(shí)現(xiàn)之后,我們會(huì)有所發(fā)現(xiàn):它們所提供的功能正是由前面講到的互斥鎖和原子操作來實(shí)現(xiàn)的。這個(gè)實(shí)現(xiàn)并不復(fù)雜。其使用的技巧包括衛(wèi)述語句、雙重檢查鎖定,以及對(duì)共享標(biāo)記的原子讀寫操作。在熟知了本章講述的這些同步工具之后,我們是否也能輕易設(shè)計(jì)出這樣簡單、有效的解決方案呢?
總之,sync.Once類型及其方法實(shí)現(xiàn)了“只會(huì)執(zhí)行一次”的語義。我們?cè)谛枰瓿芍恍杌蛑荒軋?zhí)行一次的任務(wù)的時(shí)候應(yīng)該首先想到它。
- PC官方版
- 安卓官方手機(jī)版
- IOS官方手機(jī)版