Buffered Channel + sync.WaitGroup 实现对 goroutinue 并发数的限制

写过爬虫的都应该考虑这个问题:不能毫无节制、毫无节操的无限制的并发爬取目标网站的内容,不然很容易被对方识别出来从而被封。

Go 的 goroutinue 机制使用起来非常简单,所以很多人比如我就喜欢用 Go 来写写爬虫。那么如何限制 goroutinue 并发数呢?

下面提供一种 Buffered Channel + sync.WaitGroup 实现对 goroutinue 并发数进行控制的思路。这种方式其实在 D&K 所著的《The Go Programing Language》一书中的第八章(goroutinue and channels) 中也有提到。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"fmt"
"sync"
"time"
)

func main() {

wg := &sync.WaitGroup{}

limiter := make(chan bool, 10)
for i := 0; i < 100; i++ {
wg.Add(1)
limiter <- true
go download(i, limiter, wg)
}
wg.Wait()
}

func download(index int, limiter chan bool, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("start to download :", index)
time.Sleep(1 * time.Second)
<-limiter
}

这段代码中,sync.WaitGroup 主要用来管理 goroutinue ,而 limiter 这个带有缓冲的通道则是用来控制并发数。

由于通道的阻塞特性,当并发数达到规定的阈值(创建通道时指定的缓冲容量)之后, limiter<-true就会阻塞,其后面的 go download(i,limiter,wg)就不会被执行到,达到了暂停产生 goroutinue 的效果,实现了对 goroutinue 并发数的控制。

当其中一个 download goroutinue 完成的时候,从 limiter 中释放了一个空位,此时,被 limiter 所阻塞的 for 循环得以继续执行,从而继续产生新的 goroutinue。

0%