gpt4 book ai didi

go - 围棋练习之旅#10 : Crawler

转载 作者:IT老高 更新时间:2023-10-28 13:05:02 27 4
gpt4 key购买 nike

我正在参加 Go Tour,感觉除了并发之外我对这门语言已经有了很好的理解。

slide 10是一个要求读者并行化网络爬虫的练习(并使其不包括重复,但我还没有到达那里。)

这是我目前所拥有的:

func Crawl(url string, depth int, fetcher Fetcher, ch chan string) {
if depth <= 0 {
return
}

body, urls, err := fetcher.Fetch(url)
if err != nil {
ch <- fmt.Sprintln(err)
return
}

ch <- fmt.Sprintf("found: %s %q\n", url, body)
for _, u := range urls {
go Crawl(u, depth-1, fetcher, ch)
}
}

func main() {
ch := make(chan string, 100)
go Crawl("http://golang.org/", 4, fetcher, ch)

for i := range ch {
fmt.Println(i)
}
}

我的问题是,我应该把 close(ch) 调用放在哪里。

如果我在 Crawl 方法的某处放置一个 defer close(ch),那么程序最终会从一个生成的 goroutine 写入一个封闭的 channel ,因为对 Crawl 的调用将在生成的 goroutine 之前返回。

如果我省略了对 close(ch) 的调用,正如我所演示的那样,程序会在覆盖 channel 的主函数中死锁,因为当所有 goroutines 返回时, channel 永远不会关闭。

最佳答案

查看 Effective Go 的并行化部分导致解决方案的想法。本质上,您必须在函数的每个返回路径上关闭 channel 。实际上这是 defer 语句的一个很好的用例:

func Crawl(url string, depth int, fetcher Fetcher, ret chan string) {
defer close(ret)
if depth <= 0 {
return
}

body, urls, err := fetcher.Fetch(url)
if err != nil {
ret <- err.Error()
return
}

ret <- fmt.Sprintf("found: %s %q", url, body)

result := make([]chan string, len(urls))
for i, u := range urls {
result[i] = make(chan string)
go Crawl(u, depth-1, fetcher, result[i])
}

for i := range result {
for s := range result[i] {
ret <- s
}
}

return
}

func main() {
result := make(chan string)
go Crawl("http://golang.org/", 4, fetcher, result)

for s := range result {
fmt.Println(s)
}
}

与您的代码的本质区别在于,每个 Crawl 实例都有自己的返回 channel ,而调用者函数在其返回 channel 中收集结果。

关于go - 围棋练习之旅#10 : Crawler,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13217547/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com