Go deadlock 初體驗(yàn)

為了學(xué)習(xí) Go 通過(guò) channel 實(shí)現(xiàn)同步的機(jī)制弦叶,我寫(xiě)了以下代碼來(lái)試驗(yàn):

import (
 "fmt"
 "sync"
)
?
var (
 counter int64
 wg      sync.WaitGroup
)
?
func main() {
 ch := make(chan int64)
?
 wg.Add(2)
?
 go incCounter(ch)
 go incCounter(ch)
 ch <- counter
?
 // wait until two goroutines exit
 wg.Wait()
?
 // expected value is 4
 fmt.Println("Final Counter:", counter)
}
func incCounter(ch chan int64) {
 defer wg.Done()
?
 for count := 0; count < 2; count++ {
 // receive data from channel
 value := <-ch
?
 value++
 counter = value
?
 // send data to channel
 ch <- counter
 }
}

上面的代碼很簡(jiǎn)單,就是有兩個(gè) goroutine 共享counter這個(gè)變量進(jìn)行讀寫(xiě)操作,counter最終的輸出值應(yīng)該是4。為了避免出現(xiàn)不同步的情況续崖,把counter放入一個(gè)無(wú)緩沖的 channel 中,通過(guò)這個(gè) channel 在兩個(gè) goroutine 之間傳遞counter团搞。

運(yùn)行之后程序報(bào)錯(cuò):

fatal error: all goroutines are asleep - deadlock!

這意味著所有 goroutine 都被阻塞了严望,整個(gè)程序進(jìn)入死鎖狀態(tài),可是為什么逻恐?

第一個(gè)坑

一開(kāi)始我是百思不得其解像吻,于是就在 Stack Overflow 上求助,有位哥們一語(yǔ)道破天機(jī)复隆。他解釋道拨匆,上面這段程序的執(zhí)行流程是這樣的:

deadlock.jpg

原來(lái)是沒(méi)有 goroutine 在最后從 channel 中取出counter,而這又是個(gè)無(wú)緩沖的 channel挽拂,所有 goroutine 都被阻塞了惭每,自然也就死鎖了。

解決方法

很簡(jiǎn)單亏栈,在main中把fmt.Println("Final Counter:", counter)修改為:

fmt.Println("Final Counter:", <-ch)

也就是讓主 goroutine 負(fù)責(zé)取出最終的counter台腥。

第二個(gè)坑

本來(lái)以為這樣就行了,沒(méi)想到運(yùn)行之后還是死鎖绒北。我 debug 了很久之后終于找出了第二個(gè)坑黎侈。

程序中使用了sync.WaitGroup這個(gè)類(lèi)型來(lái)阻止主 goroutine 在其他子 goroutine 之前終止,也就是不讓main函數(shù)提前退出闷游。wg.Add(2)就是告訴main要等兩個(gè) goroutine 終止之后才能退出峻汉,而在 goroutine 中則是通過(guò)wg.Done()來(lái)通知main函數(shù)某個(gè) goroutine 已終止。

wg.Done()前面加上了一個(gè)defer脐往,也就是要等到incCounter中其他代碼都執(zhí)行完了才會(huì)調(diào)用wg.Done()休吠。這就是問(wèn)題所在,像上面那張圖展示的那樣业簿,goroutine 2 把counter放入 channel 中瘤礁,等著主 goroutine 取出;但在main函數(shù)中辖源, fmt.Println("Final Counter:", <-ch)之前有一句wg.Wait()蔚携,也就是說(shuō)要等wg.Wait()方法退出了才能執(zhí)行下一句。然而此時(shí)這個(gè)方法并沒(méi)有退出克饶,因?yàn)?goroutine 2 還在被ch <- counter阻塞中酝蜒,也就沒(méi)法退出循環(huán)體,那么也就沒(méi)法執(zhí)行wg.Done()矾湃!程序也就又死鎖了亡脑。

解決

必須手動(dòng)調(diào)用wg.Done()而不能依賴defer的延遲調(diào)用機(jī)制:

func incCounter(ch chan int64) {
 for count := 0; count < 2; count++ {
 // receive data from channel
 value := <-ch
?
 value++
 counter = value
?
 // if this goroutine has incremented counter twice,
 // it will exit
 if count == 1 {
 wg.Done()
 }
?
 // send data to channel
 ch <- counter
 }
}

至此,程序終于可以正常運(yùn)行,counter最后的輸出為4霉咨,兩個(gè) goroutine 實(shí)現(xiàn)了同步蛙紫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市途戒,隨后出現(xiàn)的幾起案子坑傅,更是在濱河造成了極大的恐慌,老刑警劉巖喷斋,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唁毒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡星爪,警方通過(guò)查閱死者的電腦和手機(jī)浆西,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)顽腾,“玉大人近零,你說(shuō)我怎么就攤上這事〕ぃ” “怎么了久信?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)憎瘸。 經(jīng)常有香客問(wèn)我入篮,道長(zhǎng)陈瘦,這世上最難降的妖魔是什么幌甘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮痊项,結(jié)果婚禮上锅风,老公的妹妹穿的比我還像新娘。我一直安慰自己鞍泉,他們只是感情好皱埠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著咖驮,像睡著了一般边器。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上托修,一...
    開(kāi)封第一講書(shū)人閱讀 49,879評(píng)論 1 290
  • 那天忘巧,我揣著相機(jī)與錄音,去河邊找鬼睦刃。 笑死砚嘴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播际长,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耸采,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了工育?” 一聲冷哼從身側(cè)響起虾宇,我...
    開(kāi)封第一講書(shū)人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎如绸,沒(méi)想到半個(gè)月后文留,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竭沫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年燥翅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜕提。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡森书,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谎势,到底是詐尸還是另有隱情凛膏,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布脏榆,位于F島的核電站猖毫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏须喂。R本人自食惡果不足惜吁断,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坞生。 院中可真熱鬧仔役,春花似錦、人聲如沸是己。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卒废。三九已至沛厨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摔认,已是汗流浹背逆皮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留级野,地道東北人页屠。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓粹胯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親辰企。 傳聞我的和親對(duì)象是個(gè)殘疾皇子风纠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容