简体   繁体   English

在这个特定示例中,我应该在哪里关闭频道?

[英]Where should I close channel on this specific example?

I am writing just a simple Go pipeline, the goal is to fetch the urls and print status.我正在写一个简单的 Go 管道,目标是获取 url 和打印状态。

On fetchUrl, I need to close out channel to notify main, there will be no data comming so release the main go routine.在 fetchUrl 上,我需要关闭通道以通知 main,不会有数据传入,因此释放主 go 例程。 However i can't really close channel on fetchurl function after loop because it will be too soon.但是,我无法在循环后真正关闭 fetchurl function 上的频道,因为它会为时过早。 I don't want to add wait groups the application because whole goal is for me at the moment understand channels.我不想在应用程序中添加等待组,因为目前我的整个目标是了解渠道。

On fetchurl function, channel called two is for just making sure there will be only 2 jobs at once.在 fetchurl function 上,称为两个的通道只是为了确保一次只有 2 个作业。

package main

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

func gen(val []string) <-chan string {
    out := make(chan string, len(val))
    for _, val := range val {
        out <- val
    }
    close(out)
    return out
}

func fetchUrl(in <-chan string) <-chan string {
    out := make(chan string)
    two := make(chan struct{}, 2)
    fmt.Println("blocked")
    for url := range in {
        two <- struct{}{}
        go fetchWorker(url, two, out)
    }

    return out
}

func fetchWorker(url string, two chan struct{}, out chan string) {
    res, err := http.Get("https://" + url)
    if err != nil {
        panic(err)
    }
    <-two
    out <- fmt.Sprintf("[%d] %s\n", res.StatusCode, url)
}

func main() {
    for val := range fetchUrl(gen(os.Args[1:])) {
        fmt.Println(val)
    }
}

You need to close the out channel after every result has been written to it.在将每个结果写入其中后,您需要关闭out通道。 The easiest way to tell this is when all of the worker goroutines have exited, and the easiest way to tell that in turn is by using a sync.WaitGroup .最简单的方法是当所有的工作 goroutine 都退出时,而最简单的方法是使用sync.WaitGroup (In Go, channels and goroutines are very closely related concepts, so goroutine management is part of working with channels.) (在 Go 中,通道和 goroutine 是非常密切相关的概念,所以 goroutine 管理是使用通道的一部分。)

In the existing code, you can tie that into your fetchUrl function:在现有代码中,您可以将其绑定到您的fetchUrl function:

var wg sync.WaitGroup
for url := range in {
    two <- struct{}{}
    wg.Add(1)
    go func() {
        defer wg.Done()
        fetchWorker(url, two, out)
    }()
}
wg.Wait()
close(out)

The other structural problem you'll have with your code as written is that both gen and fetchUrl create channels, run all of the code that is supposed to write to the channels, and return the channel only after those writers finish;编写代码时遇到的另一个结构性问题是genfetchUrl都创建通道,运行所有应该写入通道的代码,并且只有在这些编写者完成后才返回通道; since nothing can read from the channel before the function returns, this will lead to deadlocks.由于在 function 返回之前无法从通道读取任何内容,这将导致死锁。 You can get around this by creating all of the channels at the top level and passing them into the generator functions.您可以通过在顶层创建所有通道并将它们传递给生成器函数来解决此问题。

If you want exactly two workers reading from the same queue of URLs, a standard pattern is to just launch two goroutines reading and writing from the same channels.如果您想要两个工作人员从同一个 URL 队列中读取,标准模式是启动两个从同一个通道读取和写入的 goroutine。 For example, you could rewrite fetchWorker as例如,您可以将fetchWorker重写为

func fetchWorker(urls <-chan string, out chan<- string) {
    for url := range urls {
        res, err := http.Get("https://" + url)
        if err != nil {
            panic(err)
        }
        out <- fmt.Sprintf("[%d] %s\n", res.StatusCode, url)
    }
}

At the top level, create the channels, create the workers, feed the input, and consume the output.在顶层,创建通道、创建工人、提供输入并使用 output。

func main() {
    urls := make(chan string)
    out := make(chan string)

    // Launch a goroutine to feed data into urls, then
    // close(urls), then stop
    go gen(os.Args[1:], urls)

    // Launch worker goroutines
    workerCount := 2
    var wg sync.WaitGroup
    for i := 0; i < workerCount; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            fetchWorker(urls, out)
        }()
    }

    // Launch a dedicated goroutine to close the channel
    go func() {
        wg.Wait()
        close(out)
    }()

    // Read the results
    for result := range(out) {
        fmt.Println(result)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM