Golang Web Crawler解决方案,2个数据竞争,退出状态66

标签 go synchronization web-crawler mutex

我一直在关注这个solution .当我运行竞争检测器时没有检测到竞争条件。但是当我用我的代码运行 race detecter 时,它给出了以下错误:

================== WARNING: DATA RACE Read at 0x00c42006c1e0 by goroutine 6: main.Crawl.func1() /task2.go:50 +0x53

Previous write at 0x00c42006c1e0 by main goroutine: main.Crawl() /task2.go:48 +0x692 main.main() /task2.go:66 +0x8c

Goroutine 6 (running) created at: main.Crawl() /task2.go:49 +0x61e main.main() /task2.go:66 +0x8c ================== . . . ================== WARNING: DATA RACE Read at 0x00c420094070 by goroutine 8: main.Crawl.func1() /task2.go:50 +0x53

Previous write at 0x00c420094070 by goroutine 6: main.Crawl() /task2.go:48 +0x692 main.Crawl.func1() /task2.go:51 +0x240

Goroutine 8 (running) created at: main.Crawl() /task2.go:49 +0x61e main.Crawl.func1() /task2.go:51 +0x240

Goroutine 6 (running) created at: main.Crawl() /task2.go:49 +0x61e main.main()

/task2.go:66 +0x8c

Found 2 data race(s) exit status 66

以下是我的代码,谁能告诉我哪里出错了。我一直想弄明白这么久,但无法确定。

        var visited = struct {
        urls map[string]bool
        sync.Mutex
    }{urls: make(map[string]bool)}

    func Crawl(url string, depth int, fetcher Fetcher) {

        if depth <= 0 {
            return
        }

        visited.Lock()
        if visited.urls[url] && visited.urls[url] == true {
            fmt.Println("already fetched: ", url)

            visited.Unlock()
            return
        }
        visited.urls[url] = true
        visited.Unlock()

        body, urls, err := fetcher.Fetch(url)

        if err != nil {
            fmt.Println(err)
            return
        }
        done := make(chan bool)

        for _, nestedUrl := range urls {
            go func(url string, d int) {
                fmt.Printf("-> Crawling child %v of %v with depth %v \n", nestedUrl, url, depth)
                Crawl(url, d, fetcher)
                done <- true

            }(nestedUrl, depth-1)
        }
        for i := range urls {
            fmt.Printf("<- [%v] %v/%v Waiting for child %v.\n", url, i, len(urls))
            <-done
        }
        fmt.Printf("<- Done with %v\n", url)
    }

    func main() {
        Crawl("http://golang.org/", 4, fetcher)

        fmt.Println("Fetching stats\n--------------")

        for url, err := range visited.urls {
            if err != true {
                fmt.Printf("%v failed: %v\n", url, err)
            } else {
                fmt.Printf("%v was fetched\n", url)
            }
        }
  }

最佳答案

您正在调用 Crawl,它会触发一个 go 例程进行递归,然后您正在访问 protected 映射,而 main 中没有互斥体,它在一些爬行完成之前执行。关于风格的几点:

  • 更喜欢同步 api
  • 将被访问的结构负责锁定(无公共(public)锁)
  • 在 main 中使用 WaitGroup 或 channel 等待完成

所以开始同步,然后弄清楚如何最好地更改为异步。然后,您只需将 go 放在同步爬网功能前面即可使其同步。查看原始游览,它与此解决方案不太相似,因此我不确定这是一个值得遵循的好模型。调用者不必锁定或担心竞争,因此您需要重新设计。我将从 original tour exercise 重新开始.

对于锁,我会使用

type T struct {
data map[string]bool
mu sync.Mutex // not just sync.Mutex
}

T 决定何时需要锁定,并具有调整数据状态或搜索数据的功能。这使得考虑使用 Lock 变得更简单,并且不太可能出错。

关于Golang Web Crawler解决方案,2个数据竞争,退出状态66,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46749498/

相关文章:

go - 将重试策略变成可重用的函数

templates - 我可以在 Golang 的另一个模板中使用模板变量吗?

并发问题(死锁?)

javascript - 使用 Jquery/Javascript 使用加/减按钮同步两个输入

例行公事 - 为什么 websocket 报告连接已关闭?

linux - 使用 GOLANG 获取系统唯一 UUID

javascript - jQuery 及其 .getJSON 不工作以及如何同步异步 .getJSON?

php - 如何从.html 页面中提取链接和标题?

hadoop - 使用Nutch Content Limit的建议

python - 列出 import.io 中的提取器