这段代码有什么问题及何解?
代码如下:
// case2_test.go
package case2
import "testing"
func TestCase2(t *testing.T) {
ch1 := make(chan int)
ch2 := make(chan int)
for i := 0; i < 9; i++ {
go func(i int) {
ch1 <- i
}(i)
}
for i := 0; i < 10; i++ {
go func(i int) {
ch2 <- i
}(i)
}
for {
select {
case x1 := <-ch1:
t.Logf("x1 = %d", x1)
case x2 := <-ch2:
t.Logf("x2 = %d", x2)
default:
t.Logf("break")
break
}
}
}
没有 default 时报错如下:
fatal error: all goroutines are asleep - deadlock!
有 default 时, 执行测试, 内存会一直增加, 而没有任何输出!
哪位能给解答下嘛?
无default时,通道无法正常读写,运行时监测到此情况抛出goroutine死锁异常退出。select是随机执行任意分支,在你使用了defaut时,就变成了一个无阻塞循环输出default内容。问题的关键在于主goroutine(函数main)要留出一定的时间,让通道所在的goroutine有机会执行。所以需要用到sleep与goto,前者是为了阻塞,后者是防止给的时间片被随机叠加,用完了直接跳出循环。代码如下
首先在我的电脑上运行的结果是这样的,简化了一下都循环两次。go版本1.14。

当没有default时,goroutine取出无缓冲通道中唯一的值后,无缓冲通道将变为空,之后任何尝试从空通道获取值的goroutine都将被阻塞并进入休眠状态。在这里,for循环中x1和x2想分别从通道ch1和通道ch2取出值,都会等待有一个goroutine在相应的通道中放入值。而放入值以后,下一个循环的goroutine想要放入值,必须等待这个通道的值在for循环中被取出。然而,我们两个通道只会被放入两次值,分别被放入两次值以后,放入值的goroutine已经全都执行完毕。通道ch1和ch2将永远不会再有值,而for循环只能永远的等待下去。在检测到这一情况后,以前版本的Go运行时环境会抛出死锁错误。(1.14版本没有报,而只是阻塞,具体哪个版本修改的待查阅)。
当有default时,我们只看到默认分支打印的输出:这是因为程序调用select太早了,以至于通道ch1和ch2还没来得及接受goroutine传给它们的值。需要在每次迭代之前加上1μs的延迟,从而使通道能够正常接收goroutine发送给它们的值。比如下面这样:
另外,break在go中可以跳出for, switch和select,所以这里跳出的是select,而不是无限循环。
而且很多时候,一般会将通道作为参数传给函数;使用等待组等待所有goroutine结束防止程序比goroutine更早结束;使用关闭通道(close(c))来退出带有select语句的无限循环。比如下面这样的方式退出无限循环,通道被关闭后ok1、ok2将被设为false