Hi,大家好,我是明哥,
在自己學習 Golang 的這段時間里,我寫了詳細的學習筆記放在我的個人微信公眾號 《Go編程時光》,對于 Go 語言,我也算是個初學者,因此寫的東西應該會比較適合剛接觸的同學,如果你也是剛學習 Go 語言,不防關注一下,一起學習,一起成長,
我的在線博客:http://golang.iswbm.com
我的 Github:github.com/iswbm/GolangCodingTime
剛接觸 Go 語言的信道的時候,經常會遇到死鎖的錯誤,而導致這個錯誤的原因有很多種,這里整理了幾種常見的,
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 創建信道的時候,若不傳遞第二個引數,則你定義的是無緩沖信道,而對于無緩沖信道,在接收者未準備好之前,發送操作是阻塞的.
因此,對于解決此問題有兩種方法:
- 使接收者代碼在發送者之前執行
- 使用緩沖信道,而不使用無緩沖信道
第一種方法:
若要程式正常執行,需要保證接收者程式在發送資料到信道前就進行阻塞狀態,修改代碼如下
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)
}
}
系列導讀
01. 開發環境的搭建(Goland & VS Code)
02. 學習五種變數創建的方法
03. 詳解資料型別:****整形與浮點型
04. 詳解資料型別:byte、rune與string
05. 詳解資料型別:陣列與切片
06. 詳解資料型別:字典與布爾型別
07. 詳解資料型別:指標
08. 面向物件編程:結構體與繼承
09. 一篇文章理解 Go 里的函式
10. Go語言流程控制:if-else 條件陳述句
11. Go語言流程控制:switch-case 選擇陳述句
12. Go語言流程控制:for 回圈陳述句
13. Go語言流程控制:goto 無條件跳轉
14. Go語言流程控制:defer 延遲呼叫
15. 面向物件編程:介面與多型
16. 關鍵字:make 和 new 的區別?
17. 一篇文章理解 Go 里的陳述句塊與作用域
18. 學習 Go 協程:goroutine
19. 學習 Go 協程:詳解信道/通道
20. 幾個信道死鎖經典錯誤案例詳解
21. 學習 Go 協程:WaitGroup
22. 學習 Go 協程:互斥鎖和讀寫鎖
23. Go 里的例外處理:panic 和 recover
24. 超詳細解讀 Go Modules 前世今生及入門使用
25. Go 語言中關于包匯入必學的 8 個知識點
26. 如何開源自己寫的模塊給別人用?
27. 說說 Go 語言中的型別斷言?
28. 這五點帶你理解Go語言的select用法

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/6564.html
標籤:Go
