Go chan 为啥没有判断 close 的接口?

本文探讨了Go语言中为何没有提供判断channel是否关闭的接口,分析了关闭channel的原理,提出了通过读取channel来间接判断的方法,并讨论了如何优雅地关闭channel,包括使用panic-recover、sync.Once和事件同步策略。文章强调了保证关闭channel时序的重要性,推荐使用context和select来实现安全的通道管理。
摘要由CSDN通过智能技术生成

更多干货,关注公众号奇伢云存储

Go 为什么没有判断 close 的接口?

在这里插入图片描述

相信大家初学 golang chan 的时候应该都遇到过 “send on closed channel” 的 panic 。这个 panic 是当你意图往一个已经 close 的 channel 里面投递元素的时候触发。那么你当你第一次遇到这个问题是否想过 channel 是否能提供一个接口方法来判断是否已经 close 了?我想过这个问题,但是把 chan 的源代码翻了个遍没有找到。为什么?

我先 hold 这个问题,我们捋一下跟 channel close 相关的事情,主要思考到 3 个问题:

  1. 关闭 channel 究竟做了什么 ?
  2. 怎么避免 close channel 导致的 panic 问题 ?
  3. 怎么优雅的关闭 channel ?

关闭 channel 究竟做了什么?

首先,用户可以 close channel,如下:

c := make(chan int)
// ...
close(c)

用 gdb 或者 delve 调试下就能发现 close 一个 channel,编译器会转换成 closechan 函数,在这个函数里是关闭 channel 的全部实现了,我们可以分析下。

closechan

对应编译函数为 closechan ,该函数很简单,大概做 3 个事情:

  1. 标志位置 1 ,也就是 c.closed = 1
  2. 释放资源,唤醒所有等待取元素的协程;
  3. 释放资源,唤醒所有等待写元素的协程;
func closechan(c *hchan) {
   
	// 以下为锁内操作
	lock(&c.lock)
	// 不能重复 close 一个 channel,否则 panic
	if c.closed != 0 {
   
		unlock(&c.lock)
		panic(plainError("close of closed channel"))
	}

	// closed 标志位置 1
	c.closed = 1

	var glist gList
	// 释放所有等待取元素的 waiter 资源
	for {
   
		// 等待读的 waiter 出队
		sg := c.recvq.dequeue()
		// 资源一个个销毁
		if sg.elem != nil {
   
			typedmemclr(c.elemtype, sg.elem)
			sg.elem = nil
		}
		gp := sg.g
		gp.param = nil
		//  相应 goroutine 加到统一队列,下面会统一唤醒

		glist.push(gp)
	}

	// 释放所有等待写元素的 waiter 资源(他们之后将会 panic)
	for {
   
		// 等待写的 waiter 出队
		sg := c.sendq.dequeue()
		// 资源一个个销毁
		sg.elem = nil
		gp := sg.g
		gp.param = nil
		// 对应 goroutine 加到统一队列,下面会统一唤醒
		glist.push(gp)
	}
	unlock(&c.lock)

	// 唤醒所有的 waiter 对应的 goroutine (这个协程列表是上面 push 进来的)
	for !glist.empty() {
   
		gp := glist.pop()
		gp.schedlink = 0
		goready(gp, 3)
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值