如何在golang go例程中从一个通道读取输入并将输出写入另一个通道?

问题描述 投票:0回答:1

我正在学习 golang,我想知道是否可以让 goroutine 从一个通道读取输入并将输出写入另一个通道,同时它受 waitgroup 限制?

请看下面的代码

func downloaderInputUrlFromChannel(id int, urlChan chan string, responseChan chan int, wg *sync.WaitGroup) {

    for url := range urlChan {
        resp, _ := http.Get(url)
        fmt.Println("STARTED worker ", id, " For URL ", url, "With Response Code", resp.StatusCode)
        responseChan <- resp.StatusCode
        fmt.Println("FINISHED worker ", id, " For URL ", url, "With Response Code", resp.StatusCode)

    }
    defer wg.Done()
}

func main() {
    urls := []string{....}  // Containing 25 urls
    MaxGoroutineCount := 5
    var wg sync.WaitGroup
    urlChan := make(chan string)
    responseChan := make(chan int)

    // Start Workers
    for i := 0; i < MaxGoroutineCount; i++ {
        wg.Add(1)
        go downloaderInputUrlFromChannel(i, urlChan, responseChan, &wg)
    }

    // Fill in the queues
    for _, url := range urls {
        urlChan <- url
    }

    close(urlChan)
    for i := 0; i < len(urls); i++ {
        fmt.Println(<-responseChan)
    }
    close(responseChan)
    wg.Wait()

我没有看到第 FINISHED 行的输出,输出为

STARTED worker  0  For URL  https://httpbin.org?q=1,l=2,p=1 With Response Code 200
STARTED worker  4  For URL  https://httpbin.org/ With Response Code 200
STARTED worker  2  For URL  https://httpbin.org?q=1,r=2,p=1 With Response Code 200
STARTED worker  1  For URL  https://httpbin.org?q=1 With Response Code 200
STARTED worker  3  For URL  https://httpbin.org?a=1 With Response Code 200

因此,responseChan 通道永远不会被填满,即使我在主通道中使用来自该通道的消息,程序也会卡在此时。如果我完全删除responseChan,程序将按预期工作。并且比顺序运行更快。

我需要 downloaderInputUrlFromChannel 的输出,所以我需要一些频道。 谁能帮助我我做错了什么?

go concurrency channel waitgroup
1个回答
0
投票

快速修复:使用

responseChan
创建
make(chan int, len(urls))

让我们从如何处理渠道开始。对于它们两者,容量都缺失(在

make
内置函数中),因此通道是无缓冲的[1]

Channel:通道的缓冲区用指定的缓冲区初始化 容量。如果为零或省略大小,则通道是无缓冲的。

这意味着如果在发送之前没有准备好接收器,则执行将被阻止。因此,正如您所知,

urls
存在最大数量,可以按如下方式创建通道:
make(chan int, len(urls))
。对于
urlChan
来说并不重要,因为有 5 个 goroutine 接收数据。

正如 @Gimby 提到的,在完全使用完通道后关闭通道是有意义的。在您的情况下,您可以在创建通道后立即推迟

close
调用。尽管如此,它仍然可以工作,因为您在关闭通道之前发送了所有
urls
,并且在收到最后发送的值之前不会发生实际关闭[2]

© www.soinside.com 2019 - 2024. All rights reserved.