[英]Where should I close channel on this specific example?
我正在写一个简单的 Go 管道,目标是获取 url 和打印状态。
在 fetchUrl 上,我需要关闭通道以通知 main,不会有数据传入,因此释放主 go 例程。 但是,我无法在循环后真正关闭 fetchurl function 上的频道,因为它会为时过早。 我不想在应用程序中添加等待组,因为目前我的整个目标是了解渠道。
在 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)
}
}
在将每个结果写入其中后,您需要关闭out
通道。 最简单的方法是当所有的工作 goroutine 都退出时,而最简单的方法是使用sync.WaitGroup
。 (在 Go 中,通道和 goroutine 是非常密切相关的概念,所以 goroutine 管理是使用通道的一部分。)
在现有代码中,您可以将其绑定到您的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)
编写代码时遇到的另一个结构性问题是gen
和fetchUrl
都创建通道,运行所有应该写入通道的代码,并且只有在这些编写者完成后才返回通道; 由于在 function 返回之前无法从通道读取任何内容,这将导致死锁。 您可以通过在顶层创建所有通道并将它们传递给生成器函数来解决此问题。
如果您想要两个工作人员从同一个 URL 队列中读取,标准模式是启动两个从同一个通道读取和写入的 goroutine。 例如,您可以将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)
}
}
在顶层,创建通道、创建工人、提供输入并使用 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.