Mặc dù sync.waitGroup
(wg) là phương thức chuẩn nhưng nó yêu cầu bạn thực hiện ít nhất một số wg.Add
cuộc gọi của mình trước khi wg.Wait
tất cả hoàn thành. Điều này có thể không khả thi đối với những thứ đơn giản như trình thu thập thông tin web, nơi bạn không biết trước số lượng cuộc gọi đệ quy và phải mất một lúc để truy xuất dữ liệu thúc đẩy wg.Add
cuộc gọi. Sau cùng, bạn cần tải và phân tích cú pháp trang đầu tiên trước khi biết kích thước của loạt trang con đầu tiên.
Tôi đã viết một giải pháp bằng cách sử dụng các kênh, tránh waitGroup
trong giải pháp của tôi là Bài tập về chuyến đi của trình thu thập thông tin web . Mỗi khi một hoặc nhiều quy trình bắt đầu, bạn sẽ gửi số tới children
kênh. Mỗi khi một quy trình di chuyển sắp hoàn thành, bạn gửi 1
đến done
kênh. Khi tổng số con bằng tổng số đã làm, chúng ta hoàn thành.
Mối quan tâm duy nhất còn lại của tôi là kích thước được mã hóa cứng của results
kênh, nhưng đó là hạn chế của Go (hiện tại).
// 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
}
}
}
}
Mã nguồn đầy đủ cho giải pháp