Golang如何用channel实现通知机制_Golang并发事件处理示例

1次阅读

chan Struct{} 是事件通知最轻量方式,零内存占用,关闭即广播;需用 select+default 避免死锁,禁用重复 close,跨层级取消应优先用 context。

Golang如何用channel实现通知机制_Golang并发事件处理示例

chan struct{} 做信号通知最轻量

不需要传递数据,只关心“发生了”这件事时,chan struct{} 是唯一推荐方式。它不占内存(struct{} 零尺寸),关闭 channel 即可广播通知,接收方用 select + defaultfor range 感知。

常见错误是误用 chan boolchan int 传哨兵值,既浪费内存又容易因未读完导致 sender 阻塞。真正只需要“事件到达”语义时,别传任何东西。

  • 初始化:done := make(chan struct{})
  • 通知:在需要触发处执行 close(done)(只能关一次)
  • 等待: 会立即返回(channel 已关闭),或用 select { case 非阻塞检查
  • 多个 goroutine 同时读 安全,全部会立刻解除阻塞

避免 select 中漏写 default 导致死锁

当 channel 通知只是“可选响应”,而你又不想让 goroutine 卡住时,必须显式加 default 分支。否则如果 channel 尚未关闭或无数据,select 会永久阻塞——尤其在循环中极易演变成资源泄漏。

典型场景:后台任务监听退出信号,但也要定期做点别的事。这时候不能只写 case 。

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

  • 错误写法:select { case → 一旦 done 没关,永远卡住
  • 正确写法:select { case
  • 若需超时控制,改用 case 替代 default

context.Context 和裸 chan struct{} 怎么选

context.WithCancel 返回的 ctx.Done() 本质也是 chan struct{},但它自带层级取消、超时、截止时间等能力。裸 channel 更适合内部模块间简单解耦;跨函数调用、涉及超时或需要传递取消链时,必须用 context

混用会导致 cancel 行为不可控:比如你手动 close(ch),但上层 context 还没被 cancel,下游可能收不到一致信号。

  • 用裸 chan struct{}:模块内状态同步(如 worker 启动完成、配置重载完成)
  • context.Contexthttp handler、数据库查询、长连接心跳等需统一生命周期管理的场景
  • 不要把 ctx.Done() 和自定义 done chan struct{} 在同一逻辑里混着等,除非你明确知道谁负责 close 且顺序可靠

关闭已关闭的 chan struct{} 会 panic

这是最容易被忽略的运行时错误:close() 只能调用一次,重复 close 同一个 channel 会直接 panic: “close of closed channel”。不像发送到已关闭 channel 只是 panic,这里连判断都难加——因为 channel 本身不可检测是否已关。

解决思路不是加锁判断,而是从设计上确保只有一个地方有权限关闭。

  • chan struct{} 包装成结构体字段,并只暴露 Close() 方法(内部用 sync.Once 保证幂等)
  • 或者改用 context.WithCancel(),它的 CancelFunc 天然幂等
  • 绝不要在多个 goroutine 里各自尝试 close(ch),哪怕加了 if ch != nil 也无效

实际并发通知逻辑里,最麻烦的往往不是怎么发,而是谁来关、什么时候关、关完还能不能读——这些细节不提前想清楚,跑一阵子就出竞态或 panic。

text=ZqhQzanResources