我一直关注这个solution。当我运行比赛检测器时没有检测到竞争条件。但是,当我使用我的代码运行race Detecter时,它会出现以下错误:
==================警告:数据竞争通过goroutine 6读取0x00c42006c1e0:main.Crawl.func1() /task2.go:50 + 0x53
以前通过main goroutine写入0x00c42006c1e0:main.Crawl() /task2.go:48 + 0x692 main.main() /task2.go:66 + 0x8c
Goroutine 6(正在运行)创建于:main.Crawl() /task2.go:49 + 0x61e main.main() /task2.go:66 + 0x8c ==================。 。 。 ==================警告:数据竞争由goroutine 8读取0x00c420094070: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(正在运行)创建于:main.Crawl() /task2.go:49 + 0x61e main.Crawl.func1() /task2.go:51 + 0x240
Goroutine 6(正在运行)创建于: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)
}
}
}
答案 0 :(得分:0)
你正在调用Crawl,它会触发一个go例程来递归,然后你正在访问受访的受保护映射而没有在main中使用互斥锁,这是在一些爬行完成之前执行的。关于风格的几点:
所以开始同步,然后弄清楚如何最好地改变为异步。然后,只需在同步爬网功能前放置即可使其同步。看一下原始的巡演,它并不像这个解决方案,所以我不确定这是一个很好的模型。呼叫者不必锁定或担心比赛,因此您需要重新设计。我将从original tour exercise重新开始。
对于锁定,我使用
type T struct {
data map[string]bool
mu sync.Mutex // not just sync.Mutex
}
并且T决定何时需要锁定并具有调整数据状态或搜索数据的功能。这样可以更容易地考虑使用Lock,而不太可能出错。