แม้ว่าsync.waitGroup
(wg) จะเป็นวิธีที่ยอมรับได้ แต่ก็จำเป็นต้องให้คุณwg.Add
โทรอย่างน้อยที่สุดก่อนที่wg.Wait
จะดำเนินการทั้งหมด สิ่งนี้อาจไม่สามารถทำได้สำหรับสิ่งง่ายๆเช่นโปรแกรมรวบรวมข้อมูลเว็บซึ่งคุณไม่ทราบจำนวนการโทรซ้ำล่วงหน้าและต้องใช้เวลาสักพักในการดึงข้อมูลที่กระตุ้นการwg.Add
โทร ท้ายที่สุดคุณต้องโหลดและแยกวิเคราะห์หน้าแรกก่อนที่คุณจะทราบขนาดของหน้าย่อยชุดแรก
ฉันเขียนวิธีแก้ปัญหาโดยใช้ช่องทางโดยหลีกเลี่ยงwaitGroup
การใช้Tour of Go -แบบฝึกหัดโปรแกรมรวบรวมข้อมูลเว็บในโซลูชันของฉัน ทุกครั้งที่เริ่มกิจวัตรประจำวันอย่างน้อยหนึ่งรายการคุณจะส่งหมายเลขไปที่children
ช่อง ทุกครั้งที่กิจวัตรการเดินทางกำลังจะเสร็จสิ้นคุณจะส่ง1
ไปที่done
ช่อง เมื่อผลรวมของเด็กเท่ากับผลรวมของการทำเราก็ทำเสร็จแล้ว
ข้อกังวลเดียวที่เหลืออยู่ของฉันคือขนาดของresults
ช่องแบบฮาร์ดโค้ดแต่นั่นเป็นข้อ จำกัด (ปัจจุบัน) Go
// 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
}
}
}
}
ซอร์สโค้ดแบบเต็มสำหรับโซลูชัน