1 回答

TA貢獻1834條經驗 獲得超8個贊
select
沒有 case 的語句是default
阻塞的case
,直到可以執行至少一個語句中的讀取或寫入。因此,您select
將阻塞,直到可以從通道讀取quit
(如果通道關閉,則為值或零值)。語言規范提供了對此行為的具體描述,特別是:
如果一個或多個通信[在陳述中表達
case
]可以繼續,則通過統一的偽隨機選擇選擇一個可以繼續的通信。否則,如果存在默認情況,則選擇該情況。如果沒有默認情況,則“select”語句將阻塞,直到至少有一個通信可以繼續進行。
其他代碼問題
警告!break
適用于select
聲明
但是,即使您確實關閉了quit
通道以發出關閉程序的信號,您的實施也可能不會產生預期的效果。對 的(未標記的)調用break
將終止函數內最內層的for
orselect
語句switch
的執行。在這種情況下,select
語句將中斷,for
循環將再次運行。如果quit
被關閉,它將永遠運行直到其他東西停止程序,否則它將再次阻塞select
關閉quit
將(可能)不會立即停止程序
正如time.Sleep
所指出的,在循環的每次迭代中調用都會阻塞一秒鐘,因此任何通過關閉來停止程序的嘗試都會在 goroutine 檢查和退出quit
之前延遲大約一秒鐘。quit
在程序停止之前,這個睡眠周期不太可能必須完全完成。
更慣用的 Go 會在select
從兩個渠道接收的語句中阻塞:
頻道
quit
_返回的通道
time.After
– 此調用是圍繞 a 的抽象Timer
,它會休眠一段時間,然后將值寫入提供的通道。
解決
更改最少的解決方案
通過對代碼進行最少更改來解決您的直接問題的解決方案是:
通過向語句添加默認情況,使讀取成為
quit
非阻塞的select
。確保 goroutine在讀取成功時實際
quit
返回:標記
for
循環并使用標記調用break
;?或者return
f1
在需要退出時從函數中獲?。ㄊ走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
? ? }
}
- 1 回答
- 0 關注
- 135 瀏覽
添加回答
舉報