我正在经历“A Go of Go”,并且一直在编辑大多数类(class),以确保我完全理解它们。我对以下练习的答案有疑问:https://tour.golang.org/concurrency/10,可以在此处找到:https://github.com/golang/tour/blob/master/solutions/webcrawler.go
我对以下部分有疑问:
done := make(chan bool)
for i, u := range urls {
fmt.Printf("-> Crawling child %v/%v of %v : %v.\n", i, len(urls), url, u)
go func(url string) {
Crawl(url, depth-1, fetcher)
done <- true
}(u)
}
for i, u := range urls {
fmt.Printf("<- [%v] %v/%v Waiting for child %v.\n", url, i, len(urls), u)
<-done
}
fmt.Printf("<- Done with %v\n", url)
从 channel
done
中添加和删除true并运行两个单独的for循环有什么目的?只是阻塞直到go例程完成?我知道这是一个示例练习,但是这种做法是否首先克服了新线程的不足?为什么没有第二个for循环和
go Crawl(url, depth-1, fetcher)
channel 就不能直接调用done
?是否因为所有变量共享内存空间?谢谢!
最佳答案
第一个for
循环调度了多个goroutine来运行,并正在一片URL上进行迭代。
第二个循环在每个URL上阻塞,等待直到其对应的Crawl()
调用完成。所有Crawl()
程序将并行运行并执行其工作,并阻止退出,直到主线程有机会在done
channel 上收到每个URL的消息。
我认为,实现此目标的更好方法是使用 sync.WaitGroup
。除非Crawl()
锁定,否则此代码可能会记录错误的内容,具体取决于每次fetcher
调用花费的时间。
如果要确定完成Crawl()
的网址,可以将完成的 channel 的类型更改为string
,并在url
完成后发送true
而不是Crawl()
。然后,我们可以在第二个循环中接收url
。
例子:
done := make(chan string)
for _, u := range urls {
fmt.Printf("-> Crawling %s\n", u)
go func(url string) {
Crawl(url, depth-1, fetcher)
done <- url
}(u)
}
for range urls {
fmt.Printf("<- Waiting for next child\n")
u := <-done
fmt.Printf(" Done... %s\n", u)
}
关于go - 'Tour of Go' Webcrawler练习中的 channel 说明,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46147914/