Obwohl sync.waitGroup
(wg) der kanonische Weg nach vorne ist, müssen Sie zumindest einige Ihrer wg.Add
Anrufe vor Ihnen tätigen, wg.Wait
damit alle fertig sind. Dies ist möglicherweise für einfache Dinge wie einen Webcrawler nicht möglich, bei denen Sie die Anzahl der rekursiven Aufrufe im Voraus nicht kennen und es eine Weile dauert, bis die Daten abgerufen sind, die die wg.Add
Aufrufe steuern. Schließlich müssen Sie die erste Seite laden und analysieren, bevor Sie die Größe des ersten Stapels untergeordneter Seiten kennen.
Ich habe eine Lösung über Kanäle geschrieben und waitGroup
in meiner Lösung die Tour of Go - Web-Crawler- Übung vermieden . Jedes Mal, wenn eine oder mehrere Go-Routinen gestartet werden, senden Sie die Nummer an den children
Kanal. Jedes Mal, wenn eine Go-Routine abgeschlossen ist, senden Sie eine 1
an den done
Kanal. Wenn die Summe der Kinder gleich der Summe der erledigten Kinder ist, sind wir fertig.
Meine einzige verbleibende Sorge ist die fest codierte Größe des results
Kanals, aber das ist eine (aktuelle) Go-Einschränkung.
// recursionController is a data structure with three channels to control our Crawl recursion.
// Tried to use sync.waitGroup in a previous version, but I was unhappy with the mandatory sleep.
// The idea is to have three channels, counting the outstanding calls (children), completed calls
// (done) and results (results). Once outstanding calls == completed calls we are done (if you are
// sufficiently careful to signal any new children before closing your current one, as you may be the last one).
//
type recursionController struct {
results chan string
children chan int
done chan int
}
// instead of instantiating one instance, as we did above, use a more idiomatic Go solution
func NewRecursionController() recursionController {
// we buffer results to 1000, so we cannot crawl more pages than that.
return recursionController{make(chan string, 1000), make(chan int), make(chan int)}
}
// recursionController.Add: convenience function to add children to controller (similar to waitGroup)
func (rc recursionController) Add(children int) {
rc.children <- children
}
// recursionController.Done: convenience function to remove a child from controller (similar to waitGroup)
func (rc recursionController) Done() {
rc.done <- 1
}
// recursionController.Wait will wait until all children are done
func (rc recursionController) Wait() {
fmt.Println("Controller waiting...")
var children, done int
for {
select {
case childrenDelta := <-rc.children:
children += childrenDelta
// fmt.Printf("children found %v total %v\n", childrenDelta, children)
case <-rc.done:
done += 1
// fmt.Println("done found", done)
default:
if done > 0 && children == done {
fmt.Printf("Controller exiting, done = %v, children = %v\n", done, children)
close(rc.results)
return
}
}
}
}
Vollständiger Quellcode für die Lösung