You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 15:39:02