【Go】channel超時機制觸發的(fatal error: all goroutines are asleep - deadlock!)
阿新 • • 發佈:2021-05-06
學習channel超時機制時,有下面這段程式碼
這一段內容詳情可見:
http://c.biancheng.net/view/4361.html
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新開一個協程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超時")
quit <- true
break
}
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second)
}
<-quit
fmt.Println("程式結束")
}
之後,我把程式碼改了一下,把for迴圈睡的時間改成了3秒,想看看直接觸發超時,結果就出問題了
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新開一個協程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超時")
quit <- true
break
}
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second * 3)
}
<-quit
fmt.Println("程式結束")
}
百度查了一下這個報錯的原因:
在main goroutine線,期望從管道中獲得一個數據,而這個資料必須是其他goroutine線放入管道的
但是其他goroutine線都已經執行完了(all goroutines are asleep),那麼就永遠不會有資料放入管道。
所以,main goroutine線在等一個永遠不會來的資料,那整個程式就永遠等下去了。
這顯然是沒有結果的,所以這個程式就說“算了吧,不堅持了,我自己自殺掉,報一個錯給程式碼作者,我被deadlock了”
回過頭來看改之後的程式碼,for迴圈中沉睡了3s,所以<-quit是還沒有執行到的,也就是說quit通道的資訊沒有接收端。而協程中,由於沉睡觸發的往quit通道傳送資訊就因為缺少接收端,所以出現上面的報錯。
解決方法有兩種:
1.把quit通道改為有快取空間的,讓它在還未接收時,資訊能先發到快取空間。也就是改為
quit := make(chan bool, 2)
這裡for迴圈中有3次,但實驗發現,快取空間為2就行了。我猜測應該是第三次的時候,還未來的及執行傳送訊號,main主協程中就執行到了quit的接收端。
2.把quit通道的接收端也放在一個協程裡面,這樣就不受main邏輯的影響。下面展示本方法的程式碼。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
//新開一個協程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <-time.After(2 * time.Second):
fmt.Println("超時")
quit <- true
break
}
}
}()
go func(){
for {
<-quit
fmt.Println("get quit")
}
}()
for i := 0; i < 3; i++ {
ch <- i
time.Sleep(time.Second * 3)
}
//<-quit
fmt.Println("程式結束")
}