如何在并发操作中关闭通道?

Posted

技术标签:

【中文标题】如何在并发操作中关闭通道?【英文标题】:How to close the channel in a concurrent operation? 【发布时间】:2021-11-17 21:03:00 【问题描述】:

我写了一个关于并发和通道的 go 代码⤵️

package main

import (
    "fmt"
    "net/http"
)

var links = []string
    "https://mcevik.com",
    "https://***.com",
    "https://www.linkedin.com",
    "https://github.com",
    "https://medium.com",
    "https://kaggle.com",


func getLink(link string, ch chan string) 
    if res, err := http.Get(link); err != nil 
        ch <- err.Error()
     else 
        ch <- fmt.Sprintf("[%d] - %s", res.StatusCode, link)
    


func main() 
    ch := make(chan string, len(links))

    for _, link := range links 
        go getLink(link, ch)
    

    for msg := range ch 
        fmt.Println(msg)
    

https://play.golang.org/p/Uz_k8KI6bKt

输出是这样的⤵️


在输出中我们看到程序没有终止。程序没有终止的原因是通道没有关闭,因此无法退出循环。

如何关闭频道并修复代码?

【问题讨论】:

【参考方案1】:

使用WaitGroup 监视写入完成。

    ch := make(chan string, len(links))
    var wg sync.WaitGroup
    for _, link := range links 
        wg.Add(1)
        go func()
            getLink(link, ch)
            wg.Done()
        ()
    

使用另一个例程来监听该事件并关闭通道。

    go func()
        wg.Wait()
        close(ch)
    ()
    for msg := range ch 
        fmt.Println(msg)
    

【讨论】:

谢谢,但您是否测试过您的代码,您的代码输出错误【参考方案2】:

通过将WaitGroup 添加到getLink 方法对其进行了重构,

func getLink(link string, wg *sync.WaitGroup, ch chan string)

wg.Wait() 通话后频道关闭。

go func() 
    wg.Wait()
    close(ch)
()

因此,最终版本的代码看起来像这样⤵️

package main

import (
    "fmt"
    "net/http"
    "sync"
)

var links = []string
    "https://mcevik.com",
    "https://***.com",
    "https://www.linkedin.com",
    "https://github.com",
    "https://medium.com",
    "https://kaggle.com",


func getLink(link string, wg *sync.WaitGroup, ch chan string) 
    defer wg.Done()

    if res, err := http.Get(link); err != nil 
        ch <- err.Error()
     else 
        ch <- fmt.Sprintf("[%d] - %s", res.StatusCode, link)
    


func main() 
    wg := sync.WaitGroup
    ch := make(chan string, len(links))

    for _, link := range links 
        wg.Add(1)
        go getLink(link, &wg, ch)
    

    go func() 
        wg.Wait()
        close(ch)
    ()

    for msg := range ch 
        fmt.Println(msg)
    

https://play.golang.org/p/741F8eHrhFP

【讨论】:

【参考方案3】:

如果你恰好启动了 N 个(即len(links))Go 例程,所有这些例程都必然会返回一条消息,那么最简单的方法是在关闭通道之前从通道中读取恰好 N 条消息。

不要在频道上range;当您不知道您将收到多少项目并且您想在频道关闭之前阅读时,这是最有用的。而是循环给定次数:

// main:

for _ = range links 
    fmt.Println(<-ch)


close(ch)

【讨论】:

感谢您的回答,但我不想像这样检查通道内的值。使用数组大小​​我也可以控制,但我想用通道来控制。能不能用channel做这样的控制,这样的改进可能吗? 当然你可以用一个通道来控制它,但你不会。这不是您解决问题中问题的方式。如果您有不同的问题,则应将其作为新问题提出。 通道应该在写入完成后关闭,而不是在你耗尽它后关闭。这是一个概念错误。

以上是关于如何在并发操作中关闭通道?的主要内容,如果未能解决你的问题,请参考以下文章

在 BasicAcks 中关闭 RabbitMQ 通道

如何使用 VBA 在 Access 中关闭单个表单实例?

如何在 IIS 中关闭凭据缓存?

如何在 symfony 2/3 中关闭会话?

如何在 Windows 的 package.json 中的 NPM 脚本中关闭终端?

如何在Jmeter webdriver Sampler中关闭新打开的窗口。