gpt4 book ai didi

for-loop - "Select"goroutine 内的 for 循环语句

转载 作者:IT王子 更新时间:2023-10-29 01:51:55 26 4
gpt4 key购买 nike

谁能解释一下,为什么 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)
}

输出:

f1 is working...
Program exited.

但是如果“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)
}

输出:

f1 is working...
f1 is working...
f1 is working...
f1 is working...
f1 is working...

https://play.golang.org/p/MxKy2XqQlt8

最佳答案

A select没有 default 的声明案例是阻塞,直到在 case 中的至少一个中读取或写入可以执行语句。因此,您的 select将阻塞直到从 quit 读取channel 是可能的(如果 channel 关闭,则为值或零值)。语言规范提供了一个 concrete description这种行为,特别是:

If one or more of the communications [expressed in the case statements] can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.

其他代码问题

注意! break适用于 select声明

但是,即使您确实关闭了 quit channel 来发出关闭程序的信号,您的实现可能不会产生预期的效果。对 break 的(未标记)调用将terminate execution of the inner-most for , select or switch statement函数内。在这种情况下,select语句将中断并且 for循环将再次运行。如果quit被关闭,它将永远运行直到其他东西停止程序,否则它将在 select 中再次阻塞。 ( Playground example )

关闭quit将(可能)不会立即停止程序

如前所述in the comments , 调用 time.Sleep在循环的每次迭代中阻塞一秒钟,因此任何通过关闭 quit 来停止程序的尝试在 goroutine 检查之前将被延迟大约一秒 quit并逃脱。在程序停止之前,这个 sleep 周期不太可能必须完全完成。

更多地道的 Go 会在 select 中阻塞关于从两个 channel 接收的声明:

  • quit channel
  • time.After 返回的 channel – 此调用是围绕 Timer 的抽象它会休眠一段时间,然后将值写入提供的 channel 。

决议

修改最少的解决方案

通过对代码进行最少更改来解决您眼前的问题的解决方案是:

  • 读取 quit通过向 select 添加默认大小写来实现非阻塞声明。
  • 确保 goroutine 实际在读取 quit 时返回成功:
    • 标记 for循环并使用标记调用 break ;或
    • return来自 f1在需要退出时运行(首选)

根据您的情况,您可能会发现它更符合惯用语 Go to use a context.Context 发出终止信号,并使用 sync.WaitGroup 在从 main 返回之前等待 goroutine 完成.

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)
}

Working example

(注意:我在您的 time.Sleep 方法中添加了一个额外的 main 调用,以避免在调用 close 并终止程序后立即返回。)

修复 sleep 阻塞问题

要解决有关阻止立即退出的阻塞 sleep 的其他问题,请将 sleep 移至 select 中的计时器。堵塞。修改您的 for按此循环 playground example从评论中正是这样做的:

for {
println("f1 is working...")

select {
case <-quit:
println("stopping f1")
return
case <-time.After(1 * time.Second):
// repeats loop
}
}

相关文献

关于for-loop - "Select"goroutine 内的 for 循环语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52356700/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com