如何优雅地关闭channel

本文探讨了Go语言中channel的关闭策略,提出了不那么优雅和稍微优雅的关闭方法,以及如何通过使用sync包和特殊技巧实现优雅关闭,包括多接收者和发送者场景。文章强调了维护channel关闭原则的重要性并提供了多种场景下的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

channel关闭原则(The Channel Closing Principle)

关闭channel的方法(不那么优雅)

关闭channel的方案(稍微优雅)

优雅地关闭channel的方案

1. M个接收者,1个发送者

2.一个接收者,N个发送者

3.M个接收者,N个发送者

4.“M个接收者,一个发送者”情况的变体

5.“N发送者”情况的一种变体

更多场景?

结论


本文翻译自《How to Gracefully Close Channels》

        几天前,我写了一篇文章解释了go语言中channel使用方法。这篇文章在reddit和HN上获得了很多肯定,但也有一些关于channel设计细节的批评。

对channels的设计和规则的批评如下:

  1. 在不修改channel状态的情况下,没有简单通用的方法来检查channel是否关闭。
  2. 关闭一个已经关闭的的channel会导致panic,所以如果关闭者不知道channel是否关闭,那么关闭channel是危险的。
  3. 向关闭的channel发送数据会导致panic,因此,如果发送者不知道channel是否关闭,则向channel发送数据是危险的。

        这些批评看起来合情合理(事实并非如此)。是的,确实没有内置函数来检查channel是否已经关闭。

        如果没有任何数据被发送到(并且将被发送到)channel,那么确实有一种简单的方法可以检查channel是否关闭。该方法已在上一篇文章中介绍。这里,为了更好的连贯性,在下面的例子中再次列出该方法,如下:

package main
import "fmt"
type T int
func IsClosed(ch <-chan T) bool {
	select {
	case <-ch:
		return true
	default:
	}

	return false
}

func main() {
	c := make(chan T)
	fmt.Println(IsClosed(c)) // false
	close(c)
	fmt.Println(IsClosed(c)) // true
}

        如上所述,这不是检查channel是否关闭的通用方法。

        事实上,即使有一个内置函数来检查channel是否已关闭,它的作用也会非常有限,就像内置函数len()用于检查存储在channel缓冲区中的当前值数量一样。因为,在对此类函数的调用返回后,被检查channel的状态可能已经发生了变化,因此返回值已经无法反映测channel的最新状态。尽管如果调用closed(ch) 返回true,可以停止向chan ch发送值,但如果调用closed(ch) 返回false,则关闭channel或继续向channel发送值是不安全的。

一、channel关闭原则(The Channel Closing Principle

        使用go语言channel的一个一般原则是,不要从接收者关闭channel;如果channel有多个并发发送者,也不要关闭channel。换句话说,如果发送者channel的唯一发送者,那么我们应该只关闭发送者goroutine协程中的channel

        (下面,我们将上述原则称为channel关闭原则。)

        当然,这并不是关闭channel的普遍原则。通用原则是不要重复关闭channel或向关闭的channel发送值。如果我们可以保证没有goroutine将关闭并将值发送到非关闭的非nilchannel,那么goroutine可以安全地关闭channel但实际上接收者或者chan

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值