Go语言中,nil、关闭的channel、有数据的channel,进行读写关闭会怎么样?
Go语言中,nil、关闭的channel、有数据的channel,进行读写关闭会怎么样?
1.对nil的channel进行读和写都会造成永久性阻塞,如果是在main函数 直接fatal error,关闭发生panic。
2.对不为nil,且未关闭的channel操作,读和写都有两种情况:
- 读操作
- 成功读取:如果channel中有数据,直接读取channel,如果此时等待队列recvxq里面有goroutine,那么需要将队列头部goroutine写入channel,唤醒这个goroutine;如果channel没有数据,就尝试从写等待队列中读取数据,并做对应的唤醒操作。
- 阻塞挂起(读操作无法及时完成):channel里面没有数据并且写等待队列为空,则当前goroutine加入等待队列中,并挂起,等待唤醒。
- 写操作
- 成功写入:如果channel读等待队列不为空,则取头部goroutine,将数据直接复制给这个头部的goroutine,并将其唤醒,流程结束;否则就尝试将数据写入到channel环形缓冲中。
- 阻塞挂起(写操作无法及时完成):通道里面buf满了并且等待队列为空,则当前goroutine加入写等待队列中,并挂起,等待唤醒。
3.对已经关闭的channel进行写和关闭操作都会panic,而读取是直到读完channel中剩余的数据,还想读的话,就会获得零值。
示例代码
package mainimport ("fmt""time"
)func main() {// 1️⃣ nil channel:读写都会永久阻塞var chNil chan intfmt.Println("1️⃣ nil channel 示例:")go func() {fmt.Println("对 nil channel 写入:会永久阻塞(不建议执行)")// chNil <- 1 // 会永久阻塞}()go func() {fmt.Println("从 nil channel 读取:会永久阻塞(不建议执行)")// <-chNil // 会永久阻塞}()// close(chNil) // panic: close of nil channeltime.Sleep(500 * time.Millisecond)// 2️⃣ 非nil且未关闭的channel:读写视情况阻塞或成功ch := make(chan int, 1)fmt.Println("\n2️⃣ 未关闭的channel 示例:")// 写入(不会阻塞,因为有缓冲)ch <- 10fmt.Println("写入10成功")// 读取(有数据,立即返回)val := <-chfmt.Printf("读取到:%d\n", val)// 再读取一次(无数据,会阻塞)go func() {fmt.Println("尝试读取空channel,会阻塞直到有写入")v := <-chfmt.Println("读取完成:", v)}()time.Sleep(500 * time.Millisecond)fmt.Println("向channel写入20,解除阻塞")ch <- 20time.Sleep(500 * time.Millisecond)// 3️⃣ 已关闭的channel:写/关panic,读零值fmt.Println("\n3️⃣ 已关闭的channel 示例:")ch2 := make(chan int, 2)ch2 <- 1ch2 <- 2close(ch2)// ✅ 连续读取已关闭的channel,直到数据读完,之后返回零值for i := 0; i < 3; i++ {v, ok := <-ch2fmt.Printf("读取:v=%v, ok=%v\n", v, ok)}// ❌ 写入已关闭的channel会panicdefer func() {if r := recover(); r != nil {fmt.Println("捕获到 panic:", r)}}()fmt.Println("尝试写入已关闭的channel...")ch2 <- 3 // panic: send on closed channel
}