Golang-如何优雅的关闭channel?

如何优雅的关闭channel?

channel关闭原则

"如何优雅的关闭channel?",这种问题经常在面试题中出现,究其原因,是因为channel创建容易,但是关闭"不易",原因有以下三点:

  • 关闭时机未知,在不改变channel自身状态的条件下,无法知道他是否已经关闭
  • 不能无脑关闭,如果一个channel已经关闭,重复关闭channel会导致panic
  • 往一个关闭的channel写数据,也会导致panic

根据上面的三个不易,可以整理出三条channel的关闭原则:

  • 不要在消费者端关闭channel
  • 不要在有多个并行的生产者时关闭channel
  • 应该只在唯一或者最后一个生产者协程中关闭channel

简单解释一下这三个原则:

  • 第一个原则,消费者不知道不知道channel何时该关闭,如果关闭了已经关闭的channel,会导致panic,而且分布式应用通常有多个消费者,每个消费者的行为一致,这么多消费者都尝试关闭channel必然会导致panic
  • 第二个原则,如果有多个生产者往channel中写数据,如果一个生产者关闭了channel,其他生产者还在往channel里写,必然会导致panic
  • 第三个原则,只要坚持这个原则,就可以确保向已经关闭的channel中发送数据的情况不会发生

"邪门歪道"式关闭channel

  • defer+recover
  • sys.Once,确保只会关闭一次
  • sys.Mutex,确保只会关闭一次

优雅的关闭channel

根据生产者和消费者的数量关系,总共可以分为四种情况:

  • 单生产者,单消费者
  • 单生产者,多消费者
  • 多生产者,单消费者
  • 多生产者,多消费者

下面我们逐个分析这四种情况,应该如果优雅关闭channle

情况1:单生产者,单消费者

  • 直接让生产者关闭channel即可

情况2:单生产者,多消费者

  • 同样直接让生产者关闭channel即可

情况三:多生产者,单消费者

1.生产者会主动退出情况

主动退出:生产者执行完循环,主动退出goroutine

  • 等生产者都发送完后在另外一个goroutine中关闭channel,可利用sync.WaitGroup通知关闭另外goroutine关闭channel,也可利用额外的channle
  • 不可以直接在生产者中关闭channle,因为生产者有多个,贸然关闭会导致某个生产者向channle中发送数据,导致panic

2.生产者不会主动退出情况

  • 消费者通过一个额外的channle来通知生产者停止生产数据,但数据channel没有被任何goroutine持有时,GC会帮我们关闭channel并回收

情况四,多生产者,多消费者

1.生产者会主动退出情况

  • 同样等生产者都发送完后在另外一个goroutine中关闭channle,可利用sync.WaitGroup或额外channel

2.生产者不会主动退出情况,消费者会主动退出情况

  • 等消费者都退出之后,通知生产者停止生产数据,等待数据channel不被任何goroutine持有时,GC会帮我们关闭channel并回收

3.生产者不会主动退出情况,消费者不会主动退出情况

  • 针对这种生产者和消费者都不主动退出的情况,我们应该想办法让消费者退出,比如说让消费者监听一个退出信号,有退出信号时就退出,然后就可以演变成消费者会退出的情况进行处理了

总结

  • 任何时候都不应该违反channel的关闭原则,如果你违反了,建议重新设计一下程序,或者思考一下,是不是该重构了
posted @ 2022-01-06 11:19  西*风  阅读(542)  评论(0编辑  收藏  举报