golang中for select时,如果channel关闭会怎么样?

发布时间 2023-06-06 16:38:39作者: 99号的格调

首先,如果对于一个已经关闭的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,来避免此类情况发生!