日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Go 知识点(09)— for select 作用于 channel

發(fā)布時間:2023/11/28 生活经验 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go 知识点(09)— for select 作用于 channel 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. for select 作用于未關(guān)閉的通道

1.1 沒有 default 分之場景

先看下面代碼

func main() {ch := make(chan int, 3)go func() {time.Sleep(2 * time.Second) // 延遲往通道里里面發(fā)送數(shù)據(jù)ch <- 1}()for {select {case v, ok := <-ch:fmt.Printf("v=%v, ok=%v\n", v, ok)time.Sleep(1 * time.Second)}fmt.Println("waiting")}
}

執(zhí)行代碼輸出結(jié)果如下:

v=1, ok=true
waiting
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan receive]:
main.main()/home/wohu/project/go/src/demo/demo.go:17 +0x82
exit status 2

從結(jié)果我們可以看到:

  • 當(dāng)通道中沒有數(shù)據(jù)時,且 select 語句沒有 default 分之時,會一直阻塞在 case 語句中;
  • 當(dāng)通道中有數(shù)據(jù)時,case 語句拿到通道里面的值之后,繼續(xù)執(zhí)行 select 語句塊之外的其余 for 循環(huán)體語句;
  • 當(dāng)通道里面的數(shù)據(jù)被取走之后,case 語句一直等待從通道中取數(shù)據(jù),但是一直沒有數(shù)據(jù)發(fā)送過來就會造成死鎖;

怎么避免這個死鎖問題呢? 這就需要加上 default 分之了。

1.2 有 default 分之場景

查看下面代碼

func main() {ch := make(chan int, 3)go func() {time.Sleep(2 * time.Second) // 延遲往通道里里面發(fā)送數(shù)據(jù)ch <- 1}()for {select {case v, ok := <-ch:fmt.Printf("v=%v, ok=%v\n", v, ok)time.Sleep(1 * time.Second)default:fmt.Println("通道沒有數(shù)據(jù)")time.Sleep(1 * time.Second)}fmt.Println("waiting")}
}

運行代碼輸出結(jié)果如下:

通道沒有數(shù)據(jù)
waiting
通道沒有數(shù)據(jù)
waiting
v=1, ok=true
waiting
通道沒有數(shù)據(jù)
waiting
通道沒有數(shù)據(jù)

從結(jié)果我們可以看到:

  • 當(dāng)通道中沒有數(shù)據(jù)時,走 default 分之,default 分之完成后會繼續(xù)執(zhí)行 for 循環(huán)體的其它語句;
  • 當(dāng)通道中有數(shù)據(jù),則會執(zhí)行對應(yīng)的 case 分之;
  • 當(dāng)通道再一次沒有數(shù)據(jù)時,則繼續(xù)會執(zhí)行 default 分之和剩余的其它for 循環(huán)體語句,而且會一直死循環(huán)執(zhí)行;

2. for select 作用于關(guān)閉的通道

2.1 對關(guān)閉的通道執(zhí)行 case 會造成死循環(huán)

繼續(xù)下面代碼

func main() {ch := make(chan int, 3)go func() {time.Sleep(2 * time.Second) // 延遲往通道里里面發(fā)送數(shù)據(jù)ch <- 1close(ch)}()for {select {case v, ok := <-ch:fmt.Printf("v=%v, ok=%v\n", v, ok)time.Sleep(1 * time.Second)default:fmt.Println("通道沒有數(shù)據(jù)")time.Sleep(1 * time.Second)}fmt.Println("waiting")}
}

運行輸出結(jié)果

通道沒有數(shù)據(jù)
waiting
通道沒有數(shù)據(jù)
waiting
v=1, ok=true
waiting
v=0, ok=false
waiting
v=0, ok=false
...

注意我們在前面已經(jīng)將通道關(guān)閉,這個時候的 case 語句依然成立,所以會形成死循環(huán)執(zhí)行這個 case 語句。

那么怎樣能跳出這個死循環(huán)的 case 語句呢?

2.2 跳出死循環(huán)的 case 語句

要跳出這個死循環(huán)的 case 語句,我們需要在 case 中通過第二個參數(shù)判斷 chan 是否關(guān)閉,如果關(guān)閉則通過 make(chan type) 來將關(guān)閉的 channil ,當(dāng)再次執(zhí)行到 select 時,因為 channil 會進入阻塞。

select 中如果任意某個分之可讀(包括 default ),它就會被執(zhí)行,其他被忽略。所以在有 default 分之場景時, select 會跳過這個阻塞 case ,去執(zhí)行 default 分之,這樣就可以避開這個死循環(huán)的 case 分之。

func main() {ch := make(chan int, 3)go func() {time.Sleep(2 * time.Second) // 延遲往通道里里面發(fā)送數(shù)據(jù)ch <- 1close(ch)}()for {select {case v, ok := <-ch:if !ok {ch = make(chan int)fmt.Println("通道已經(jīng)關(guān)閉")} else {fmt.Printf("v=%v, ok=%v\n", v, ok)time.Sleep(1 * time.Second)}default:fmt.Println("通道沒有數(shù)據(jù)")time.Sleep(1 * time.Second)}fmt.Println("waiting")}
}

輸出結(jié)果如下:

通道沒有數(shù)據(jù)
waiting
通道沒有數(shù)據(jù)
waiting
v=1, ok=true
waiting
通道已經(jīng)關(guān)閉
waiting
通道沒有數(shù)據(jù)
waiting
...

會一直循環(huán)打印 default 分之的輸出,那怎樣跳出這個循環(huán)呢?

2.3 跳出 for select 循環(huán)語句

  • 可以使用 gotolable 跳轉(zhuǎn)到 for 外面;
  • 可以設(shè)置一個額外的標(biāo)記位,當(dāng) chan 關(guān)閉時,設(shè)置 flag=true ,在 for 的最后判斷 flag 決定是否 break

我們采用第二種方案:

func main() {ch := make(chan int, 3)go func() {time.Sleep(2 * time.Second) // 延遲往通道里里面發(fā)送數(shù)據(jù)ch <- 1close(ch)}()exitFlag := falsefor {select {case v, ok := <-ch:if !ok {ch = make(chan int)fmt.Println("通道已經(jīng)關(guān)閉")exitFlag = true} else {fmt.Printf("v=%v, ok=%v\n", v, ok)time.Sleep(1 * time.Second)}default:fmt.Println("通道沒有數(shù)據(jù)")time.Sleep(1 * time.Second)}if exitFlag {fmt.Println("跳出循環(huán)")break}fmt.Println("waiting")}
}

輸出結(jié)果

通道沒有數(shù)據(jù)
waiting
通道沒有數(shù)據(jù)
waiting
v=1, ok=true
waiting
通道已經(jīng)關(guān)閉
跳出循環(huán)

由以上示例我們可以得出以下結(jié)論:

  1. select 語句中如果任意某個 case 的通道有值可讀時,它就會被執(zhí)行,其他 case 會被忽略;
  2. 如果沒有 default 語句,select 將有可能阻塞,直到某個 case 分之有值可以運行,所以 select 里最好有一個 default ,否則將有一直阻塞的風(fēng)險;

如果 select 語句發(fā)現(xiàn)同時有多個候選分支滿足選擇條件,那么它就會用一種偽隨機的算法在這些分支中選擇一個并執(zhí)行。

僅當(dāng) select 語句中的所有 case 表達式都被求值完畢后,它才會開始選擇候選分支。這時候,它只會挑選滿足選擇條件的候選分支執(zhí)行。如果所有的候選分支都不滿足選擇條件,那么默認(rèn)分支就會被執(zhí)行。如果這時沒有默認(rèn)分支,那么 select 語句就會立即進入阻塞狀態(tài),直到至少有一個候選分支滿足選擇條件為止。一旦有一個候選分支滿足選擇條件,select 語句就會被喚醒,這個候選分支就會被執(zhí)行。

總結(jié)

以上是生活随笔為你收集整理的Go 知识点(09)— for select 作用于 channel的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。