Go并发编程中channel怎么用_Go通道通信机制讲解

18次阅读

必须用 make 初始化 channel 才能发送或接收,nil channel 操作会 panic;无缓冲 channel(make(chan T)或 make(chan T, 0))同步阻塞,缓冲 channel 异步通信,容量不能为负。

Go 并发编程中 channel 怎么用_Go 通道通信机制讲解

channel 必须初始化才能发送或接收

声明一个 chan int 变量只是创建了 nil channel,对它做 sendrecv 会立即 panic:”panic: send on nil channel” 或 “fatal error: all goroutines are asleep - deadlock“。必须用 make 初始化,并指定缓冲区大小(0 表示无缓冲)。

常见写法:

ch := make(chan int)          // 无缓冲,同步通信 ch := make(chan string, 10)   // 缓冲容量为 10,异步通信

注意:make(chan T)make(chan T, 0) 效果相同,但后者语义更明确;缓冲区大小不能为负,传负数会 panic。

无缓冲 channel 的阻塞行为决定协程协作节奏

无缓冲 channel 的 操作是同步的:发送方会阻塞,直到有接收方就绪;接收方也一样。这天然适合“等待完成”“配对协作”场景,比如启动 goroutine 后等它结束:

done := make(chan bool) go func() {     // 做一些工作     time.Sleep(100 * time.Millisecond)     done <- true>

容易踩的坑:

  • 在同一个 goroutine 中对无缓冲 channel 先发后收(或先收后发),必然死锁
  • 多个 goroutine 同时向一个无缓冲 channel 发送,但只有一个接收者,其余发送者永久阻塞
  • 忘记关闭 channel 导致 range 无限等待(见下一条)

range channel 要求 channel 关闭,否则永远阻塞

for v := range ch 本质是持续接收,直到 channel 关闭且缓冲区为空。如果没人调用 close(ch),循环永远不会退出——即使所有发送者已退出,只要 channel 没关,range 就卡住。

典型模式是“扇出 - 扇入”(fan-out/fan-in):

func fanIn(chs ……<-chan int) <-chan int { out := make(chan for _, ch :11 =range chs go func(c v :21 =range c>

关键点:

  • 只有 sender 应该调用 close(),receiver 调用会 panic
  • close() 只能调用一次,重复 close panic
  • 关闭后仍可接收(返回零值 +ok=false),但不可再发送

select + timeout 是避免 channel 永久阻塞的标准解法

生产环境几乎不会让 goroutine 在 channel 上无限等待。用 select 配合 time.Aftercontext.WithTimeout 实现超时控制:

select {case v := <-ch:     fmt.Println("received:", v) case <-time.After(2 * time.Second):     fmt.Println("timeout") }

注意:

  • select 默认随机选择一个就绪 case;所有 channel 都未就绪时,若含 default 则立即执行,否则阻塞
  • time.After 创建新 timer,频繁调用可能泄漏;高频率场景建议复用 time.NewTimer
  • 涉及 context 时,优先用 替代 time.After,便于主动取消

channel 的核心不是“传递数据”,而是“协调执行时机”。理解阻塞点在哪、谁负责关闭、如何设防超时,比记住语法更重要。

text=ZqhQzanResources