如何在并发操作中关闭通道?
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做这样的控制,这样的改进可能吗? 当然你可以用一个通道来控制它,但你不会。这不是您解决问题中问题的方式。如果您有不同的问题,则应将其作为新问题提出。 通道应该在写入完成后关闭,而不是在你耗尽它后关闭。这是一个概念错误。以上是关于如何在并发操作中关闭通道?的主要内容,如果未能解决你的问题,请参考以下文章