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
在需要退出时从函数中获取(首选)
根据您的情况,您可能会发现 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 关注
- 96 浏览
添加回答
举报