当 Go 的 channel 被 close 后读写操作会怎么样?

当 Go 的 channel 被 close 后,对它的读操作行为有以下明确规则:


重点行为总结

操作 行为
从已关闭且仍有缓存数据的 channel 读 会正常返回该数据
从已关闭且缓冲区数据已读完的 channel 读 返回 零值 (基于类型)和 ok=false
向已关闭的 channel 写数据 ❌ 直接 panic

如果 channel 没有缓冲 和有缓冲的读完数据后的操作是一样的结果


示例代码

go 复制代码
ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)

v, ok := <-ch // v=1, ok=true
v, ok = <-ch // v=2, ok=true
v, ok = <-ch // v=0, ok=false

range 遍历已关闭 channel

当你使用 range 遍历 channel 时,它会一直读到 channel 关闭且数据读完后才退出循环:

go 复制代码
for v := range ch {
    fmt.Println(v)
}
fmt.Println("done")

零值返回示例

如果 channel 是 chan string,零值读出来就是 ""

如果是 chan *User,零值就是 nil


为什么要设计成这样?

  • 避免读端阻塞
  • 让接收端可以用 ok 判断数据是否结束
  • 使 channel 更像是一个有限流(stream)

典型使用场景

生产者关闭 channel 通知消费者不再有新数据

go 复制代码
func producer(out chan<- int) {
    defer close(out)
    for i := 0; i < 5; i++ {
        out <- i
    }
}

func consumer(in <-chan int) {
    for v := range in {
        fmt.Println(v)
    }
}

常见误区

误区 实际情况
close 后读会 panic ❌ 永远不会 panic
必须由接收方 close ❌ 通常是发送方 close
close 可重复调用 ❌ 重复 close 会 panic

完整示例代码

go 复制代码
package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 3)

    // Producer
    go func() {
        for i := 0; i < 5; i++ {
            ch <- i
        }
        close(ch) // 通知消费者:不再有新数据
    }()

    // Consumer
    for v := range ch { // 会一直读到 ch 被关闭 & 数据读完
        fmt.Println("received:", v)
    }

    fmt.Println("done") // 会执行,因为 range 自动退出
}

输出示例(顺序可能不同)

sh 复制代码
received: 0
received: 1
received: 2
received: 3
received: 4
done
相关推荐
leonardee5 分钟前
Android和JAVA面试题相关资料
java·后端
这儿有一堆花14 分钟前
python视觉开发
开发语言·python
w***42417 分钟前
Spring Boot 条件注解:@ConditionalOnProperty 完全解析
java·spring boot·后端
q***735531 分钟前
删除文件夹,被提示“需要来自 TrustedInstaller 的权限。。。”的解决方案
android·前端·后端
Jonathan Star1 小时前
JavaScript 中,原型链的**最顶端(终极原型)只有一个——`Object.prototype`
开发语言·javascript·原型模式
普通网友1 小时前
C++中的组合模式
开发语言·c++·算法
q***61501 小时前
PHP进阶-在Ubuntu上搭建LAMP环境教程
开发语言·ubuntu·php
Dneccc1 小时前
Qt5配置MSVC2017
开发语言·qt
江公望1 小时前
Qt QByteArray类型,10分钟讲清楚
开发语言·c++·qt
小灰灰搞电子1 小时前
Qt Sensors 传感器框架详解
开发语言·qt