Hi,你們好,我是明哥。git
在本身學習 Golang 的這段時間裏,我寫了詳細的學習筆記放在個人我的微信公衆號 《Go編程時光》,對於 Go 語言,我也算是個初學者,所以寫的東西應該會比較適合剛接觸的同窗,若是你也是剛學習 Go 語言,不防關注一下,一塊兒學習,一塊兒成長。github
個人在線博客: http://golang.iswbm.com
個人 Github:github.com/iswbm/GolangCodingTime
剛接觸 Go 語言的信道的時候,常常會遇到死鎖的錯誤,而致使這個錯誤的緣由有不少種,這裏整理了幾種常見的。golang
fatal error: all goroutines are asleep - deadlock!
看下面這段代碼編程
package main import "fmt" func main() { pipline := make(chan string) pipline <- "hello world" fmt.Println(<-pipline) }
運行會拋出錯誤,以下數組
fatal error: all goroutines are asleep - deadlock!
看起來好像沒有什麼問題?先往信道中存入數據,再從信道中讀取數據。微信
回顧前面的基礎,咱們知道使用 make 建立信道的時候,若不傳遞第二個參數,則你定義的是無緩衝信道,而對於無緩衝信道,在接收者未準備好以前,發送操做是阻塞的.函數
所以,對於解決此問題有兩種方法:學習
第一種方法:spa
若要程序正常執行,須要保證接收者程序在發送數據到信道前就進行阻塞狀態,修改代碼以下3d
package main import "fmt" func main() { pipline := make(chan string) fmt.Println(<-pipline) pipline <- "hello world" }
運行的時候仍是報一樣的錯誤。問題出在哪裏呢?
原來咱們將發送者和接收者寫在了同一協程中,雖然保證了接收者代碼在發送者以前執行,可是因爲前面接收者一直在等待數據 而處於阻塞狀態,因此沒法執行到後面的發送數據。仍是同樣形成了死鎖。
有了前面的經驗,咱們將接收者代碼寫在另外一個協程裏,並保證在發送者以前執行,就像這樣的代碼
package main func hello(pipline chan string) { <-pipline } func main() { pipline := make(chan string) go hello(pipline) pipline <- "hello world" }
運行以後 ,一切正常。
第二種方法:
接收者代碼必須在發送者代碼以前 執行,這是針對無緩衝信道纔有的約束。
既然這樣,咱們改使用可緩衝信道不就OK了嗎?
package main import "fmt" func main() { pipline := make(chan string, 1) pipline <- "hello world" fmt.Println(<-pipline) }
運行以後,一切正常。
每一個緩衝信道,都有容量,當信道里的數據量等於信道的容量後,此時再往信道里發送數據,就失形成阻塞,必須等到有人從信道中消費數據後,程序纔會往下進行。
好比這段代碼,信道容量爲 1,可是往信道中寫入兩條數據,對於一個協程來講就會形成死鎖。
package main import "fmt" func main() { ch1 := make(chan string, 1) ch1 <- "hello world" ch1 <- "hello China" fmt.Println(<-ch1) }
當程序一直在等待從信道里讀取數據,而此時並無人會往信道中寫入數據。此時程序就會陷入死循環,形成死鎖。
好比這段代碼,for 循環接收了兩次消息("hello world"和「hello China」)後,再也沒有人發送數據了,接收者就會處於一個等待永遠接收不到數據的囧境。陷入死循環,形成死鎖。
package main import "fmt" func main() { pipline := make(chan string) go func() { pipline <- "hello world" pipline <- "hello China" // close(pipline) }() for data := range pipline{ fmt.Println(data) } }
包子鋪裏的包子已經賣完了,可還有人在排隊等着買,若是再也不作包子,就要告訴排隊的人:不用等了,今天的包子已經賣完了,明日請早呀。
不能讓人家死等呀,不跟客人說明一下,人家還覺得大家店後面還在蒸包子呢。
因此這個問題,解決方法很簡單,只要在發送完數據後,手動關閉信道,告訴 range 信道已經關閉,無需等待就行。
package main import "fmt" func main() { pipline := make(chan string) go func() { pipline <- "hello world" pipline <- "hello China" close(pipline) }() for data := range pipline{ fmt.Println(data) } }
系列導讀
24. 超詳細解讀 Go Modules 前世此生及入門使用