golang中for select时,如果channel关闭会怎么样?
首先,如果对于一个已经关闭的channel来说,如果此时channel里还有值,则会正确读到channel里的值,且返回的第二个bool值为true;如果关闭前,channel里的值已经被读完,则最后返回的则是channel的零值;
那么针对该问题,我们通过代码来验证一下:
package main
import (
"fmt"
"time"
)
const timestamp = "2006-01-02 15:04:05"
func main() {
chan1 := make(chan int)
go func() {
time.Sleep(1 * time.Second)
chan1 <- 10
close(chan1)
}()
for {
select {
case x, ok := <-chan1:
fmt.Printf("%v,通道读取到:x=%v,ok=%v\n", time.Now().Format(timestamp), x, ok)
time.Sleep(500 * time.Millisecond)
default:
fmt.Printf("%v,通道未读取到,进入到default\n", time.Now().Format(timestamp))
time.Sleep(500 * time.Millisecond)
}
}
}
从结果中看出,我们可以继续读到channel里的值,但是读完之后,却依旧会继续执行读操作,这时候,可通过第二个参数bool值去解决该问题
for {
select {
case x, ok := <-chan1:
fmt.Printf("%v,通道读取到:x=%v,ok=%v\n", time.Now().Format(timestamp), x, ok)
time.Sleep(500 * time.Millisecond)
if !ok {
chan1 = nil
}
default:
fmt.Printf("%v,通道未读取到,进入到default\n", time.Now().Format(timestamp))
time.Sleep(500 * time.Millisecond)
}
}
从输出结果上看,该方法解决了该问题,因为将channel置为nil,在从channel去读数据的话,相当于操作一个为初始化的channel,会一直阻塞。
并且如果select里只有一个已经关闭的case的话,则会一直出现死循环的状态
for {
select {
case x, ok := <-chan1:
fmt.Printf("%v,通道读取到:x=%v,ok=%v\n", time.Now().Format(timestamp), x, ok)
time.Sleep(500 * time.Millisecond)
// if !ok {
// chan1 = nil
// }
// default:
// fmt.Printf("%v,通道未读取到,进入到default\n", time.Now().Format(timestamp))
// time.Sleep(500 * time.Millisecond)
}
}
那么针对上述场景,如果将其置为nil,又会怎么样呢?
for {
select {
case x, ok := <-chan1:
fmt.Printf("%v,通道读取到:x=%v,ok=%v\n", time.Now().Format(timestamp), x, ok)
time.Sleep(500 * time.Millisecond)
if !ok {
chan1 = nil
}
// default:
// fmt.Printf("%v,通道未读取到,进入到default\n", time.Now().Format(timestamp))
// time.Sleep(500 * time.Millisecond)
}
fmt.Println("the others information")
}
从输出结果上看,会panic,引发deadlocl问题,所以,select中最好放入一个default,来避免此类情况发生!