Golang中select的default分支为何每次都会执行三次,即使channel已返回
Golang中select的default分支为何每次都会执行三次,即使channel已返回
import "fmt"
import "time"
func ab(ch chan bool,i int) {
duration := time.After(time.Second * time.Duration(600))
for {
select {
case <- duration:
fmt.Printf(" received value: %d ",i)
break
default:
fmt.Printf(" no value received %d ",i)
ch <- true
break
}
}
}
func main() {
for i := 1; i < 10; i++{
fmt.Printf("Testcase %d start",i)
ch := make(chan bool, 1)
go ab(ch,i)
if <- ch {
fmt.Printf("Testcase %d end",i)
}
fmt.Printf("sleeping for 10 seconds")
time.Sleep(10*time.Second)
}
}
更多关于Golang中select的default分支为何每次都会执行三次,即使channel已返回的实战教程也可以访问 https://www.itying.com/category-94-b0.html
有人能解释为什么它会打印三次而不是一次吗
更多关于Golang中select的default分支为何每次都会执行三次,即使channel已返回的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢您的回复,我现在明白了,这里使用 return 而不是 break 是有效的
因为你使用了带缓冲的通道。
它可以容纳1个元素。第一次触发时它会打印然后发送。这个元素在主goroutine中立即被读取。此时通道再次变为空闲状态。
循环第二次运行时再次打印并发送。现在没有从通道读取的操作,所以通道已满。
现在进行第三次迭代。同样会发生打印,然后ab-goroutine会因为通道已满而阻塞。
你可能想要的是从fir循环中跳出而不是从select中跳出。你需要给for循环添加标签并使用带标签的break来实现这一点
在您的代码中,default分支每次循环都会执行,因为select语句会立即检查所有case的条件,如果没有任何case准备就绪(例如duration channel尚未超时),就会执行default分支。这导致default分支在每次循环迭代中都会执行,直到duration channel超时(600秒后),这解释了为什么您看到它多次执行。
问题在于您的for循环没有退出条件,除了duration超时外。在default分支中,您向ch发送了true,但main函数中的if <- ch会立即接收这个值,导致ab函数继续循环并再次进入select,重复执行default分支。
此外,break语句在select中只退出select,而不是退出for循环。因此,即使duration超时,break也不会终止循环,程序会继续运行。
以下是代码的修正版本,添加了循环退出逻辑,并解释了行为:
package main
import (
"fmt"
"time"
)
func ab(ch chan bool, i int) {
duration := time.After(time.Second * time.Duration(600))
for {
select {
case <-duration:
fmt.Printf(" received value: %d ", i)
return // 使用return退出函数,而不是break
default:
fmt.Printf(" no value received %d ", i)
ch <- true
// 注意:这里没有退出循环,所以会继续执行
}
}
}
func main() {
for i := 1; i < 10; i++ {
fmt.Printf("Testcase %d start", i)
ch := make(chan bool, 1)
go ab(ch, i)
if <-ch {
fmt.Printf("Testcase %d end", i)
}
fmt.Printf("sleeping for 10 seconds")
time.Sleep(10 * time.Second)
}
}
在原始代码中,default分支执行的原因:
durationchannel在600秒后才会发送值,因此在超时前,select总是会进入default分支。- 每次循环,
default分支向ch发送true,main函数接收后打印信息,但ab函数继续循环,导致default重复执行。 - 由于
ch是缓冲channel(容量1),第一次发送不会阻塞,但后续发送会阻塞,因为main只在每次迭代接收一次。这可能导致死锁,但您的代码中main有睡眠,所以可能不会立即显现。
如果您想限制default分支的执行次数,可以添加计数器或使用其他退出条件。例如,修改ab函数为:
func ab(ch chan bool, i int) {
duration := time.After(time.Second * time.Duration(600))
count := 0
for {
select {
case <-duration:
fmt.Printf(" received value: %d ", i)
return
default:
if count >= 3 {
return // 限制default执行3次后退出
}
fmt.Printf(" no value received %d ", i)
ch <- true
count++
}
}
}
这确保了default分支只执行三次,然后函数返回。

