Go官方指南网页爬虫练习报all goroutines are asleep死锁求排查
死锁成因及修复方案
核心错误1:waitG.Done() 属于不可达代码
你将waitG.Done()写在了return语句之后,所有Crawl函数的执行路径都不会触发计数减1的操作,WaitGroup的计数永远无法降到0,主goroutine在waitG.Wait()处永久阻塞,直接触发死锁。
核心错误2:共享缓存cache的读取未加锁
判断if cache[url]时没有持有互斥锁,并发读写map会触发数据竞争,可能导致缓存判断失效、程序运行异常。
核心错误3:所有提前返回的分支都未调用waitG.Done()
Crawl函数存在多个提前返回的路径:
- 检测到重复URL时返回
- 深度<=0时返回
- Fetch调用报错时返回
这些路径都没有执行计数减1操作,即使你把waitG.Done()移到return之前,也会出现计数漏减的问题。
修复后的Crawl函数参考
func Crawl(url string, depth int, fetcher Fetcher) { // 用defer保证所有退出路径都会执行Done defer waitG.Done() // 加锁后再读写缓存 mutex.Lock() if cache[url] { mutex.Unlock() fmt.Println("Duplicate: ", url) return } cache[url] = true mutex.Unlock() fmt.Println("None-duplicate: ", url) if depth <= 0 { return } body, urls, err := fetcher.Fetch(url) if err != nil { fmt.Println(err) return } fmt.Printf("found: %s %q\n", url, body) for _, u := range urls { waitG.Add(1) go Crawl(u, depth-1, fetcher) } }
内容的提问来源于stack exchange,提问作者Conor
相关产品推荐
相关产品推荐

