Golang-如何优雅的关闭一个Channel?

如何优雅的关闭Channel

这部分主要参考自:https://qcrao91.gitbook.io/go/channel/ru-he-you-ya-di-guan-bi-channel

直接关闭存在的问题

主要就是上述"向已关闭的Channel收发,会如何?"中所提到的情况:

1、向已关闭的channel中发送数据,会panic

2、重复关闭已经关闭的channel,会panic。

3、从已关闭的channel接收数据,收到的是0。

一个比较粗糙的实现

利用从读channel的,会返回bool的性质。

go 复制代码
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的状态进行了修改。

二来,检测的瞬间和关闭瞬间有间隔。

三来,多个同时调用的话,也可能重复关闭。

合理的方案

don't close a channel from the receiver side and don't close a channel if the channel has multiple concurrent senders.

即:不要从一个 receiver 侧关闭 channel,也不要在有多个 sender 时,关闭 channel。

因此考虑从发送端进行关闭。

单一sender的情况

即:

一个 sender,一个 receiver

一个 sender, M 个 receiver

对于这种情况,直接sender方,发完之后关了即可。

多个sender的情况

即:

N 个 sender,一个 reciver

N 个 sender, M 个 receiver

针对一个reciver的情况:

增加一个传递关闭信号的 channel,receiver 通过信号 channel 下达关闭数据 channel 指令。senders 监听到关闭信号后,停止接收数据。

go 复制代码
func main() {
    rand.Seed(time.Now().UnixNano())

    const Max = 100000
    const NumSenders = 1000

    dataCh := make(chan int, 100)
    stopCh := make(chan struct{})

    // senders
    for i := 0; i < NumSenders; i++ {
        go func() {
            for {
                select {
                case <- stopCh:
                    return
                case dataCh <- rand.Intn(Max):
                }
            }
        }()
    }

    // the receiver
    go func() {
        for value := range dataCh {
            if value == Max-1 {
                fmt.Println("send stop signal to senders.")
                close(stopCh)
                return
            }

            fmt.Println(value)
        }
    }()

    select {
    case <- time.After(time.Hour):
    }
}

注意:这个代码中,其实并没有关闭channel。这个优雅的处理就是指的:不用他了,将他交给Golang的GC机制去处理。

针对多个reciver的情况:

如果依旧采取上述方案,则可能遇到的情况是:下达了多个关闭命令,依旧造成"向已关闭的channel进行关闭"。因此使用一个"中间人"channel,reciver都向他发送stop,收到第一个后 就向sender发stop。reciver默认长度设置为:Num(senders) + Num(receivers),可以避免阻塞问题。

此部分代码见参考链接的原文即可

相关推荐
by组态1 分钟前
Ricon组态系统API参考手册
前端·后端·物联网
君顾14 分钟前
外卖CPS系统开发实战指南:从架构设计到部署全流程解析
java·开发语言·外卖
再吃一根胡萝卜17 分钟前
Agent 如何选择与生成 SQL —— Text2SQL 全链路详解
后端
苍何20 分钟前
把操作流程直接录成 Skill,豆包太夯了!
后端
Go_error39 分钟前
在 Go 服务层中如何优雅地管理事务?
后端·go
是立不是利39 分钟前
前端交互基石:深入剖析JavaScript三级联动背后的设计哲学
开发语言·前端·javascript
老刘的望远镜1 小时前
AgentScope Java 从零(03):Agent 的记性默认全开,我在第 50 轮翻了车
java·开发语言·javascript
爱读源码的大都督1 小时前
DeepSeek面试官问:多租户 RAG 系统怎样实现细粒度权限控制?
后端·面试·架构
Littlehero_1211 小时前
QT自定义控件之电气接线图(源码开源)
开发语言·qt