for range channel:仅当channel已被关闭且缓冲区内没有数据后,才会退出
测试阻塞及死锁:
当我们在main goroutine中make一个channel,通过开启一个发送方goroutine往channel里面写入数据,如下:
c1 := make(chan int, 100)
go func() {
for i := 0; i < 10; i++ {
c1 <- i
}
fmt.Println("c1 发送结束,但并未手动关闭")
time.Sleep(time.Second * 3)
fmt.Println("c1 goroutine 结束休眠")
// 当前goroutine结束
}()
上面的例子中,等最后一个打印语句结束后,发送方goroutine结束;
但接收方goroutine仍在以for range方式读取channel,由于发送方goroutine已经结束,channel不再可能被关闭,也不会再发送消息,接收方goroutine for range循环被阻塞,不再可能执行 defer语w.Done();
则main goroutine w.Wait()语句不再等待,报错死锁结束main goroutine。
go func() {
defer w.Done() // c1 未被关闭,不会跳出range,导致这一行永远阻塞,直到main goroutine结束
for v := range c1 {
fmt.Println("读取到:", v)
}
fmt.Println("退出了for range...") // 无法执行
}()
w.Wait() //发生死锁
fmt.Println("main done")
测试代码:
func blockTest() {
var w sync.WaitGroup
w.Add(1)
c1 := make(chan int, 100)
go func() {
for i := 0; i < 10; i++ {
c1 <- i
}
//close(c1)
//fmt.Println("c1 closed...")
fmt.Println("c1 发送结束,但并未手动关闭")
time.Sleep(time.Second * 3)
fmt.Println("c1 goroutine 结束休眠")
}()
go func() {
defer w.Done() // c1 未被关闭,不会跳出range,导致这一行永远阻塞,直到main goroutine结束
for v := range c1 { // 阻塞
fmt.Println("读取到:", v)
}
fmt.Println("退出了for range...")
}()
w.Wait() // 报错死锁
fmt.Println("main done")
}