https://mp.weixin.qq.com/s/Ov1FvLsLfSaY8GNzfjfMbg一文引發(fā)的延續(xù)思考
我們提供的服務有:成都網(wǎng)站制作、網(wǎng)站建設、外貿(mào)網(wǎng)站建設、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、越秀ssl等。為近千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術(shù)的越秀網(wǎng)站制作公司
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
select {
case ch <- getVal(1):
fmt.Println("in first case")
case ch <- getVal(2):
fmt.Println("in second case")
default:
fmt.Println("default")
}
}()
fmt.Println("The val:", <-ch)
}
func getVal(i int) int {
fmt.Println("getVal, i=", i)
return i
}
無論 select 最終選擇了哪個 case,getVal() 都會按照源碼順序執(zhí)行:getVal(1) 和 getVal(2),也就是它們必然先輸出:
getVal, i= 1
getVal, i= 2
package main
import (
"fmt"
"time"
)
func talk(msg string, sleep int) <-chan string {
ch := make(chan string)
go func() {
for i := 0; i < 5; i++ {
ch <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Duration(sleep) * time.Millisecond)
}
}()
return ch
}
func fanIn(input1, input2 <-chan string) <-chan string {
ch := make(chan string)
go func() {
for {
select {
case ch <- <-input1:
case ch <- <-input2:
}
}
}()
return ch
}
func main() {
ch := fanIn(talk("A", 10), talk("B", 1000))
for i := 0; i < 10; i++ {
fmt.Printf("%q\n", <-ch)
}
}
每次進入以下 select 語句時:
select {
case ch <- <-input1:
case ch <- <-input2:
}
<-input1
和 <-input2
都會執(zhí)行,相應的值是:A x 和 B x(其中 x 是 0-5)。但每次 select 只會選擇其中一個 case 執(zhí)行,所以 <-input1
和 <-input2
的結(jié)果,必然有一個被丟棄了,也就是不會被寫入 ch 中。因此,一共只會輸出 5 次,另外 5 次結(jié)果丟掉了。(你會發(fā)現(xiàn),輸出的 5 次結(jié)果中,x 比如是 0 1 2 3 4)
而 main 中循環(huán) 10 次,只獲得 5 次結(jié)果,所以輸出 5 次后,報死鎖。
如果改為這樣就一切正常:
select {
case t := <-input1:
ch <- t
case t := <-input2:
ch <- t
}
我的理解:
case ch <- <-input:
語句是分成兩段執(zhí)行的,可以理解為
t := <- input //case選擇還未明確的時候會執(zhí)行
ch <- t //如果沒有選擇此case,則不執(zhí)行此語句
并且這是兩條語句,具有先后順序
所以<-input 執(zhí)行后,沒有選擇此case,<-input的結(jié)果就會被丟棄掉,從而導致上述的死鎖問題。
上述提到
無論 select 最終選擇了哪個 case,getVal() 都會按照源碼順序執(zhí)行:getVal(1) 和 getVal(2),也就是它們必然先輸出:
getVal, i= 1
getVal, i= 2
思考一:如果getVal()方法執(zhí)行的時間不同,select的運行時長是取決于運行時間長的,還是時間的總和?
func getVal1(i int) int {
time.Sleep(time.Second * 1)
fmt.Println("getVal, i=", i)
return i
}
func getVal2(i int) int {
time.Sleep(time.Second * 2)
fmt.Println("getVal, i=", i)
return i
}
func main() {
ch := make(chan int)
go func() {
for {
beginTime := time.Now()
select {
case ch <- getVal1(1):
case ch <- getVal2(2):
default:
fmt.Println("")
}
fmt.Println(time.Since(beginTime))
}
}()
time.Sleep(time.Second * 10)
}
輸出的結(jié)果
getVal, i= 1
getVal, i= 2
3.00s
getVal, i= 1
getVal, i= 2
3.00s
getVal, i= 1
getVal, i= 2
3.00s
可以看出來,每次select都會按順序執(zhí)行case語句,并且select的執(zhí)行時間為case語句的總和
當然在實際生產(chǎn)中也不會有這種寫法
正確的寫法:
func main() {
begin := time.Now()
ch := make(chan int)
ch2 := make(chan int, 2)
go func() {
ch2 <- getVal1(1)
}()
go func() {
ch2 <- getVal2(2)
}()
go func() {
for {
select {
case d := <-ch2:
ch <- d
}
}
}()
for i := 0; i < 2; i++ {
fmt.Println(<-ch)
}
fmt.Println(time.Since(begin))
}
輸出結(jié)果,此時取決于運行時間最長的getVal()
getVal, i= 1
1
getVal, i= 2
2
2.00s
在實際生產(chǎn)中,select語句只用于接受channel中的數(shù)值,而不是去執(zhí)行某一方法
細心的小伙伴已經(jīng)發(fā)現(xiàn)了,上述的寫法有兩個bug
加點注釋看看輸出的結(jié)果
func main() {
begin := time.Now()
ch := make(chan int)
ch2 := make(chan int, 2)
go func() {
ch2 <- getVal1(1)
}()
go func() {
ch2 <- getVal2(2)
}()
time.Sleep(2 * time.Second)
fmt.Println("goroutine num", runtime.NumGoroutine())
go func() {
defer func() {
if r := recover(); r != nil {
fmt.Println("panic err", r)
}
}()
for {
select {
case d := <-ch2:
ch <- d
}
}
}()
for i := 0; i < 2; i++ {
fmt.Println(<-ch)
}
close(ch)
fmt.Println(time.Since(begin))
fmt.Println("goroutine num", runtime.NumGoroutine())
ch2 <- 1
time.Sleep(time.Second * 1)
}
輸出的結(jié)果
getVal, i= 1
getVal, i= 2
goroutine num 2
1
2
2.00s
goroutine num 2
panic err send on closed channel
可以看到,for循環(huán)的協(xié)程并沒有被釋放,并且在后續(xù)的ch <-
操作中也報出了panic異常
網(wǎng)頁題目:Go select 死鎖引發(fā)的思考
標題網(wǎng)址:http://chinadenli.net/article4/dsoisie.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、網(wǎng)站營銷、動態(tài)網(wǎng)站、Google、搜索引擎優(yōu)化、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)