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

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

相关推荐
热爱编程的小白白7 分钟前
【Playwright自动化】安装和使用
开发语言·python
听风吟丶8 分钟前
Java NIO 深度解析:从 BIO 到 NIO 的演进与实战
开发语言·python
学历真的很重要8 分钟前
LangChain V1.0 Messages 详细指南
开发语言·后端·语言模型·面试·langchain·职场发展·langgraph
sali-tec11 分钟前
C# 基于halcon的视觉工作流-章58-输出点云图
开发语言·人工智能·算法·计算机视觉·c#
lpfasd12312 分钟前
Rust + WebAssembly:让嵌入式设备被浏览器调试
开发语言·rust·wasm
lion King77614 分钟前
c++八股:explicit
开发语言·c++
初见无风17 分钟前
4.3 Boost 库工具类 optional 的使用
开发语言·c++·boost
申阳18 分钟前
Day 7:05. 基于Nuxt开发博客项目-首页开发
前端·后端·程序员
有梦想的攻城狮27 分钟前
我与C++的一面之缘
开发语言·c++
毕设源码-朱学姐35 分钟前
【开题答辩全过程】以 基于java的民宿管理小程序为例,包含答辩的问题和答案
java·开发语言·小程序