题目
在这个练习中,我们将会使用 Go 的并发特性来并行化一个 Web 爬虫。
修改 Crawl 函数来并行地抓取 URL,并且保证不重复。
提示:你可以用一个 map 来缓存已经获取的 URL,但是要注意 map 本身并不是并发安全的!
实现逻辑
- 采用锁实现互斥使用map
- 增加等待组实现等待线程结束
- 使用struct来组合map,锁,等待组三种数据
- 为上述struct增加一个exist方法,用于判断url是否存在,不存在则存入且增加等待数量
优势
网上找到同样使用锁+等待组实现的,不过等待组的增加以及访问链接map时使用了两次lock、unlock操作,而两者是可以合并的,本文就是这么处理的。
代码
package main
import (
"fmt"
"sync"
)
type Fetcher interface {
// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
Fetch(url string) (body string, urls []string, err error)
}
type SafeMap struct {
v map[string]int
mux sync.Mutex // 访问互斥锁
wg sync.WaitGroup // 等待组
}
func (c *SafeMap) exist(url string) bool {
c.mux.Lock()
defer c.mux.Unlock()
_, ok := c