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),可以避免阻塞问题。

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

相关推荐
法欧特斯卡雷特1 小时前
Kotlin 新特性抢先看:伴生扩展与伴生块
后端·面试·开源
小小龙学IT1 小时前
Go 语言 gRPC(grpc-go)深度解析
rpc·架构·golang·go
桃李醉春风1 小时前
被微信拒审那天,我才真正学会 Vibecoding:一个人 + AI,3 万行代码、1.8 万张素材的小程序全复盘
后端
CoderYanger1 小时前
一轮复习——F.动态规划模型总结(入门篇)
java·开发语言·数据结构·算法·leetcode·职场和发展·动态规划
她的男孩1 小时前
打印模板草稿能保存,一点发布就报主从关系:我们把校验拆成了两档
java·spring boot·后端
Bazingga1 小时前
Embabel学习笔记:把Agent的决策权从LLM手里拿回来
后端
用户8314550980311 小时前
基于 Firecracker 自建 Agent 沙箱集群:状态机 + 文件管理 + 进程编排,附 E2B 生产经验校准
后端
Frank_refuel1 小时前
【C++】智能指针
开发语言·c++
泡海椒2 小时前
JQuick-Excel VALIDATION 实战:用作用范围约束 Excel 导入校验
开发语言·python·excel
武子康2 小时前
LingBot-VA 2.0 深度解析:为什么要同时预测未来世界与机器人动作
人工智能·后端·agent