亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

帶有for循環的goroutine內部的“選擇”語句

帶有for循環的goroutine內部的“選擇”語句

Go
慕妹3146593 2023-04-04 15:25:37
有人可以解釋一下,為什么如果 goroutine 有無限for循環并且select在循環內部,循環中的代碼只運行一次?package mainimport (    "time")func f1(quit chan bool){    go func() {        for {            println("f1 is working...")            time.Sleep(1 * time.Second)            select{            case <-quit:            println("stopping f1")            break            }        }       }()}func main() {    quit := make(chan bool)    f1(quit)    time.Sleep(4 * time.Second)}輸出:f1 is working...Program exited.但是如果“select”被注釋掉:package mainimport (    "time")func f1(quit chan bool){    go func() {        for {            println("f1 is working...")            time.Sleep(1 * time.Second)            //select{            //case <-quit:            //println("stopping f1")            //break            //}        }       }()}func main() {    quit := make(chan bool)    f1(quit)    time.Sleep(4 * time.Second)}輸出:f1 is working...f1 is working...f1 is working...f1 is working...f1 is working...https://play.golang.org/p/MxKy2XqQlt8
查看完整描述

1 回答

?
MMMHUHU

TA貢獻1834條經驗 獲得超8個贊

select沒有 case 的語句是default阻塞case,直到可以執行至少一個語句中的讀取或寫入。因此,您select將阻塞,直到可以從通道讀取quit(如果通道關閉,則為值或零值)。語言規范提供了對此行為的具體描述,特別是:

如果一個或多個通信[在陳述中表達case]可以繼續,則通過統一的偽隨機選擇選擇一個可以繼續的通信。否則,如果存在默認情況,則選擇該情況。如果沒有默認情況,則“select”語句將阻塞,直到至少有一個通信可以繼續進行。

其他代碼問題

警告!break適用于select聲明

但是,即使您確實關閉了quit通道以發出關閉程序的信號,您的實施也可能不會產生預期的效果。對 的(未標記的)調用break將終止函數內最內層的fororselect語句switch的執行。在這種情況下,select語句將中斷,for循環將再次運行。如果quit被關閉,它將永遠運行直到其他東西停止程序,否則它將再次阻塞select

關閉quit將(可能)不會立即停止程序

正如time.Sleep所指出的,在循環的每次迭代中調用都會阻塞一秒鐘,因此任何通過關閉來停止程序的嘗試都會在 goroutine 檢查和退出quit之前延遲大約一秒鐘。quit在程序停止之前,這個睡眠周期不太可能必須完全完成。

更慣用的 Go 會在select從兩個渠道接收的語句中阻塞:

  • 頻道quit_

  • 返回的通道time.After– 此調用是圍繞 a 的抽象Timer,它會休眠一段時間,然后將值寫入提供的通道。

解決

更改最少的解決方案

通過對代碼進行最少更改來解決您的直接問題的解決方案是:

  • 通過向語句添加默認情況,使讀取成為quit非阻塞的select

  • 確保 goroutine在讀取成功時實際quit返回:

    • 標記for循環并使用標記調用break;?或者

    • returnf1在需要退出時從函數中獲?。ㄊ走x)

根據您的情況,您可能會發現 Go 更慣用地使用 a 來context.Context發出終止信號,并使用 async.WaitGroup等待 goroutine 在從 返回之前完成main。

package main


import (

? ? "fmt"

? ? "time"

)


func f1(quit chan bool) {

? ? go func() {

? ? ? ? for {

? ? ? ? ? ? println("f1 is working...")

? ? ? ? ? ? time.Sleep(1 * time.Second)


? ? ? ? ? ? select {

? ? ? ? ? ? case <-quit:

? ? ? ? ? ? ? ? fmt.Println("stopping")

? ? ? ? ? ? ? ? return

? ? ? ? ? ? default:

? ? ? ? ? ? }

? ? ? ? }

? ? }()

}


func main() {

? ? quit := make(chan bool)

? ? f1(quit)

? ? time.Sleep(4 * time.Second)

? ? close(quit)

? ? time.Sleep(4 * time.Second)

}

工作示例

(注意:我在您的方法中添加了一個額外的time.Sleep調用main,以避免在調用close和終止程序后立即返回。)

修復睡眠阻塞問題

要解決有關阻止立即退出的阻塞睡眠的其他問題,請將睡眠移動到塊中的計時器select。for根據這個游樂場示例修改循環正是這樣做的:

for {

? ? println("f1 is working...")


? ? select {

? ? case <-quit:

? ? ? ? println("stopping f1")

? ? ? ? return

? ? case <-time.After(1 * time.Second):

? ? ? ? // repeats loop

? ? }

}


查看完整回答
反對 回復 2023-04-04
  • 1 回答
  • 0 關注
  • 135 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號