Golang使用channel进行并发通信示例

4次阅读

不加 default 的 select 会永久阻塞,因它必须等待至少一个 case 就绪;若所有 channel 均不可读写且无 default,则 goroutine 陷入死锁。

Golang 使用 channel 进行并发通信示例

为什么 select 里不加 default 可能导致 goroutine 永久阻塞

Go 的 channel 是带缓冲或无缓冲的通信管道,但它的阻塞行为常被误判。比如向一个无缓冲 channel 发送数据时,若没有 goroutine 同时在另一端接收,send 操作会一直挂起——这本身是设计使然,但容易在逻辑分支中被忽略。

常见错误场景:多个 channel 等待响应,但没考虑“所有 channel 都暂不可用”的情况。

  • 无缓冲 channel 的 sendrecv 必须成对出现,否则至少一方永久等待
  • 有缓冲 channel(如 make(chan int, 1))可暂存一个值,但缓冲满后仍会阻塞 send
  • select 中不写 default,就等同于“必须等到某个 case 就绪”,没有兜底逻辑
ch := make(chan int) go func() {     time.Sleep(2 * time.Second)     ch <- 42>

如何安全关闭 channel 并避免 panic: send on closed channel

channel 关闭后不能再发送,但可以继续接收(已缓存的数据 + 零值)。错误常出现在多生产者场景下:谁该关?何时关?关早了其他 goroutine 还在发,就 panic。

原则:** 只由发送方关闭,且确保所有发送操作已完成 **。推荐用 sync.WaitGroup 协调。

立即学习 go 语言免费学习笔记(深入)”;

  • 从已关闭 channel 接收不会 panic,返回零值 + false(如 v, ok := 中 ok==false
  • 重复关闭 channel 会 panic,所以不要在多个 goroutine 里都写 close(ch)
  • 如果用 range 遍历 channel,它会在 channel 关闭且缓冲为空时自动退出
ch := make(chan int, 2) go func() {     defer close(ch) // 正确:由 sender defer 关闭     ch <- 1 ch <- 2}() for v := range {>

chan 和 类型的区别直接影响函数参数设计

Go 的 channel 类型支持方向限定:chan 表示“只能发送”, 表示“只能接收”。这不是语法糖,而是编译期检查机制,用错会报错。

典型用途:限制函数职责,防止意外写入或读取,提升接口安全性。

  • 函数参数声明为 ,调用方传入普通 chan int 都合法(协变)
  • 但不能把 传给期望 chan 的参数(方向不匹配)
  • 返回 chan int 的函数,若只想暴露接收能力,应返回
func counter(out chan<- int) {>func printer(in <-chan int) {>

time.After 配合 select 实现超时控制,但别直接传给多个 goroutine

time.After 返回一个单次触发的 ,常用于超时判断。但它不是“可重用资源”——每次调用才新建 channel,重复使用旧的 channel 会导致超时逻辑失效。

错误模式:把同一个 time.After(1*time.Second) 结果传给多个 select,第一个触发后 channel 就已读空,后续 select 永远等不到它。

  • 每个需要独立超时判断的 select 块,应调用一次 time.After
  • 若需复用超时逻辑,可用 time.NewTimer 并手动 Reset,但注意并发安全
  • 更轻量做法:在循环内每次重新生成 time.After,语义清晰且无状态管理负担
ch := make(chan string, 1) go func() {     time.Sleep(1500 * time.Millisecond)     ch <- "done"}() 

select {case msg := <-ch: fmt.println("received:", msg) case <-time.after(1 * time.second):>

实际写并发逻辑时,最易被忽略的是 channel 的生命周期归属和方向控制——不是“能通就行”,而是“谁建、谁关、谁读、谁写”必须提前约定清楚。类型系统能拦住一部分错误,但关 channel 的时机、select 的兜底、超时 channel 的复用,这些得靠结构设计来保障。

星耀云
版权声明:本站原创文章,由 星耀云 2026-01-08发表,共计844字。
转载说明:转载本网站任何内容,请按照转载方式正确书写本站原文地址。本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。