如何使用Go channel传递数据_Go通道读写规则说明

channel必须先初始化才能使用;未初始化的channel为nil,读写会永久阻塞;make(chan int)创建无缓冲channel,make(chan string, 10)创建容量为10的缓冲channel。

channel必须先初始化才能使用

未初始化的 channelnil,对它进行读或写操作会永久阻塞(goroutine 永久挂起),这是 Go 中最常见的死锁源头之一。

  • make(chan int) 创建无缓冲 channel;make(chan string, 10) 创建容量为 10 的缓冲 channel
  • 判断 channel 是否为 nil:直接用 ch == nil,但生产代码中应避免依赖此判断,而应确保初始化逻辑不被跳过
  • 函数接收 channel 参数时,调用方传入 nil channel 是合法语法,但会导致调用即阻塞 —— 所以接口文档或注释里应明确要求“非 nil”

向已关闭的 channel 写数据会 panic

Go 运行时会在向已关闭的 chan 执行 ch 时触发 panic: send on closed channel。但读已关闭的 channel 是安全的:返回零值 + false(如果带 ok 返回)。

  • 关闭 channel 的责任通常属于发送方;接收方不应 close,也不应假设自己能“最后一个关闭”
  • 多个 goroutine 并发写同一 channel 时,不能靠“谁先 close 谁赢”,必须用额外同步机制(如 sync.Once

    确保只 close 一次
  • 常见误写:
    close(ch)
    ch <- 42 // panic!

无缓冲 channel 的读写必须配对才不阻塞

无缓冲 channel 相当于一个同步点:每次 操作都要求对方 goroutine 同时准备好另一端操作,否则当前 goroutine 阻塞。

  • 发送方阻塞直到有接收方就绪;接收方阻塞直到有发送方就绪
  • 典型陷阱:在 main goroutine 中向无缓冲 channel 发送,但没启动接收 goroutine —— 程序立即死锁
  • 调试建议:用 go run -gcflags="-l" main.go 关闭内联后加 log.Println("sent"),可快速定位卡在哪一行
  • 示例(会死锁):
    ch := make(chan int)
    ch <- 1 // main 卡在这里,永远等不到接收者

range 遍历 channel 会自动等待关闭

for v := range ch 本质是持续接收,直到 channel 关闭且缓冲区为空。它隐式处理了“读完所有已发送值再退出”的逻辑,比手动 for { select { case v := 更简洁。

  • range 不会因 channel 暂时空而提前退出;它只响应 close() 信号
  • 如果发送方永不 close,range 永远不会结束 —— 所以务必确认关闭时机(例如:所有发送 goroutine 结束后)
  • 无法用 range 判断 channel 是否“正在被写入”,也无法从中途停止遍历(除非用 break 配合外部条件)
  • 带缓冲 channel 的 range 会读出所有现存值,然后阻塞等待新值或关闭;关闭后退出循环

关闭 channel 的时机和主体容易混淆,尤其在多生产者场景下 —— 它不是“谁最后发完谁关”,而是需要协调机制。别指望 runtime 帮你推断意图。