1 回答
TA贡献1770条经验 获得超3个赞
您可以使用 sync.WaitGroup(请参阅文档)来控制工作人员的生命周期,并使用非阻塞发送,以便工作人员在尝试排队更多作业时不会死锁:
package main
import "sync"
const workers = 4
type job struct{}
func (j *job) do(enqueue func(job)) {
// do the job, calling enqueue() for subtasks as needed
}
func main() {
jobs, wg := make(chan job), new(sync.WaitGroup)
var enqueue func(job)
// workers
for i := 0; i < workers; i++ {
go func() {
for j := range jobs {
j.do(enqueue)
wg.Done()
}
}()
}
// how to queue a job
enqueue = func(j job) {
wg.Add(1)
select {
case jobs <- j: // another worker took it
default: // no free worker; do the job now
j.do(enqueue)
wg.Done()
}
}
todo := make([]job, 1000)
for _, j := range todo {
enqueue(j)
}
wg.Wait()
close(jobs)
}
尝试使用缓冲通道避免死锁的困难在于,您必须预先分配一个足够大的通道,以确保在不阻塞的情况下保持所有挂起的任务。除非您有少量已知的 URL 可供抓取,否则会出现问题。
当您回退到在当前线程中进行普通递归时,您没有那个静态缓冲区大小限制。当然,仍然存在限制:如果有太多工作待处理,您可能会耗尽 RAM,理论上您可以通过深度递归耗尽堆栈(但这很难!)。因此,如果您要对整个 Web 进行爬行,则需要以更复杂的方式跟踪待处理的任务。
最后,作为一个更完整的例子,我对这段代码并不感到非常自豪,但我碰巧写了一个函数来启动一个并行排序,它以与获取 URL 的方式相同的方式递归。
- 1 回答
- 0 关注
- 168 浏览
添加回答
举报