【问题标题】:Golang Web Crawler solution, 2 data races, exit status 66Golang Web Crawler 解决方案,2 次数据竞赛,退出状态 66
【发布时间】:2018-03-26 17:54:08
【问题描述】:

我一直在关注这个solution。当我运行竞态检测器时,没有检测到竞态条件。但是当我用我的代码运行比赛检测器时,它会出现以下错误:

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

主 goroutine 在 0x00c42006c1e0 上一次写入:main.Crawl() /task2.go:48 +0x692 main.main() /task2.go:66 +0x8c

Goroutine 6 (running) 创建于: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

goroutine 6 之前在 0x00c420094070 处写入:main.Crawl() /task2.go:48 +0x692 main.Crawl.func1() /task2.go:51 +0x240

Goroutine 8 (running) 创建于:main.Crawl() /task2.go:49 +0x61e main.Crawl.func1() /task2.go:51 +0x240

Goroutine 6 (running) 创建于:main.Crawl() /task2.go:49 +0x61e main.main()

/task2.go:66 +0x8c

发现 2 个数据竞争退出状态 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)
            }
        }
  }

【问题讨论】:

  • 你能显示整个文件吗?可能在 play.google.com 或 github 上?现在还不清楚种族恐慌的界限在哪里。
  • 可能在子 goroutine Printf 调用中使用了nestedUrl
  • 如果您不共享整个文件,我们无法知道哪一行出现故障
  • 以后一定要给出行号或操场上的所有代码,因为这样会更容易。
  • 您好,感谢您的回复,这里是代码文件github.com/aneelaSaleem/Go/blob/master/task2.go

标签: go synchronization web-crawler mutex


【解决方案1】:

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

  • 首选同步 api
  • 让被访问的结构负责锁定(无公共锁)
  • 在 main 中使用等待组或通道等待完成

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

对于锁,我会使用

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

T 决定何时需要锁定,并具有调整数据状态或搜索数据的功能。这样可以更轻松地考虑使用 Lock 并且不太可能犯错。

【讨论】:

  • 感谢肯尼的回复。 “首选同步 api”是什么意思?我的意思是我已经在尝试同步“让访问的结构负责锁定(没有公共锁)”你能给我举个例子吗? “在 main 中使用一个等待组或通道来等待完成”已经在使用一个通道来等待所有 goroutine 的完成
  • 即使你打算使用 go 关键字,也要先编写一个非异步版本。这是一种特殊情况,但通常调用者决定是否调用异步。您可以使用 go 关键字将同步转换为异步,但不能将异步转换为同步。然后被调用者决定何时锁定他们的数据,如果需要锁定,不要在函数外使用 map 或暴露 Lock 函数。
猜你喜欢
  • 2016-10-11
  • 1970-01-01
  • 1970-01-01
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多