当 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
相关推荐
candyTong6 分钟前
阿里开源 AI Code Review 工具:ocr review 的执行链路解析
javascript·后端·架构
铁皮饭盒25 分钟前
TypeBox 比 Zod.js 校验 快10倍, 还兼容AI 工具调用, 他做对了什么?
前端·javascript·后端
倔强的石头_9 小时前
WorkBuddy 上手实战:打造一个可用的本地 AI 工作台
后端
苍何14 小时前
Coding 真有质的飞跃?实测下豆包seed 2.1 pro
后端
苍何14 小时前
试了下腾讯 Marvis,回不去了...
后端
caibixyy14 小时前
springboot+langchain4j 实战 Day14——工具嵌入多 Agent(Tool-Equipped Multi-Agent)
后端
caibixyy14 小时前
springboot+langchain4j 实战 Day13 多 Agent 协作(Router + 子 Agent 分流)
后端
飘尘14 小时前
前端转全栈(Java 后端)必须要知道的:开发中的锁机制与分布式并发控制
前端·后端·全栈
苍何14 小时前
清华团队做了个具身智能大脑,有点东西!
后端