同步等待组和通道的最佳实践是什么?我想在循环中处理消息和阻塞,似乎将通道的关闭委托给另一个 go 例程似乎是一个奇怪的解决方案?
func Crawl(url string, depth int, fetcher Fetcher) {
ch := make(chan string)
var waitGroup sync.WaitGroup
waitGroup.Add(1)
go crawlTask(&waitGroup, ch, url, depth, fetcher)
go func() {
waitGroup.Wait()
close(ch)
}()
for message := range ch {
// I want to handle the messages here
fmt.Println(message)
}
}
func crawlTask(waitGroup *sync.WaitGroup, ch chan string, url string, depth int, fetcher Fetcher) {
defer waitGroup.Done()
if depth <= 0 {
return
}
body, urls, err := fetcher.Fetch(url)
if err != nil {
ch <- err.Error()
return
}
ch <- fmt.Sprintf("found: %s %q\n", url, body)
for _, u := range urls {
waitGroup.Add(1)
go crawlTask(waitGroup, ch, u, depth-1, fetcher)
}
}
func main() {
Crawl("http://golang.org/", 4, fetcher)
}
// truncated from https://tour.golang.org/concurrency/10 webCrawler
Run Code Online (Sandbox Code Playgroud)
作为使用 waitgroup 和额外 goroutine 的替代方案,您可以使用单独的通道来结束 goroutine。
这也是 Go 中的惯用语。它涉及使用对照组进行阻止select。
因此,您必须创建make一个新通道,通常使用一个空结构作为其值(例如closeChan := make(chan struct{}),当关闭 ( close(closeChan)) 时,该通道将结束 goroutine 本身。
您可以使用select来阻止,直到提供数据或关闭,而不是在a上进行范围。chan
中的代码Crawl可能如下所示:
for { // instead of ranging over a to-be closed chan
select {
case message := <-ch:
// handle message
case <-closeChan:
break // exit goroutine, can use return instead
}
}
Run Code Online (Sandbox Code Playgroud)
然后在 中crawlTask,您可以关闭closeChan(作为另一个参数传入,就像ch您返回时一样(我想那就是您希望其他 goroutine 结束并停止处理消息的时候?)
if depth <= 0 {
close(closeChan)
return
}
Run Code Online (Sandbox Code Playgroud)