Dim*_*maf 1 select for-loop go goroutine
有人可以解释一下,为什么goroutine有无限for循环并且循环select内部,循环中的代码只运行一次?
package main
import (
"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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
f1 is working...
Program exited.
Run Code Online (Sandbox Code Playgroud)
但是,如果“选择”被注释掉:
package main
import (
"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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
f1 is working...
f1 is working...
f1 is working...
f1 is working...
f1 is working...
Run Code Online (Sandbox Code Playgroud)
select没有default案例的语句将阻塞,直到case可以执行对至少一个语句的读取或写入操作为止。因此,您select将阻塞直到可以从quit通道读取(如果通道关闭则为零或零值)。语言规范提供了对此行为的具体描述,特别是:
如果[
case语句中表达的] 一项或多项通信可以进行,则可以通过统一的伪随机选择来选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,则“ select”语句将阻塞,直到可以进行至少一种通信为止。
break适用于该select陈述但是,即使您确实关闭了quit通道以发出程序关闭信号,您的实现也可能不会达到预期的效果。A(未标记的)呼叫break将终止最内部的执行for,select或switch语句的功能之内。在这种情况下,该select语句将中断并且for循环将再次运行。如果quit已关闭,它将一直运行,直到其他程序停止该程序为止,否则它将再次阻塞select(在Playground示例中)
quit(可能)不会立即停止程序如注释中所述,在time.Sleep循环的每次迭代中,对块的调用将持续一秒钟,因此,通过quitgoroutine检查quit并转义之前,通过关闭停止程序的任何尝试都将延迟大约一秒钟。在程序停止之前,此睡眠期不可能完整地完成。
更多惯用的Go会阻止select从两个渠道接收的声明:
quit通道time.After–此调用返回的通道是a周围的抽象,Timer它休眠一段时间,然后将值写入所提供的通道。解决方案以最小的代码更改来解决您的即时问题,将是:
quit通过在select语句中添加默认大小写,从非阻塞状态进行读取。quit成功时返回:
for循环,并使用标记的调用break;要么return从f1当是时候退出(优选的)函数根据您的情况,您可能会发现它更惯用Go来使用context.Context信号终止,并使用Go sync.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)
}
Run Code Online (Sandbox Code Playgroud)
(注意:我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
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1081 次 |
| 最近记录: |