2017-10-14 11 views
0

私はこれに続いてsolutionです。レースディテクタを走らせると競合状態は検出されません。しかし、私は私のコードとの競合detecterを実行すると、それは次のエラーを与える:誰もが私が間違っているつもりだところを教えてくださいすることができます後Golang Webクローラーソリューション、2データレース、終了ステータス66

================== 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) 
      } 
     } 
    } 
+0

ファイル全体を表示できますか? play.google.comやgithubにいる可能性がありますか?今、それはレースパニックとそのラインがどこにあるのかは分かりません。 –

+0

子ゴルーチンの 'Printf'コールではおそらく* nestedUrlを使用しています。 –

+0

ファイル全体を共有していない場合、どの行が失敗しているのかわからない –

答えて

0

あなたは、あなたは、いくつかのクロールが完了する前に実行されるメインでミューテックスなしに訪問した保護されたマップにアクセスしている、再帰的に行ってルーチンをオフに発射クロールを呼んでいます。スタイル上のいくつかのポイント:

  • 同期APIを好む(何のパブリックロック)をロックしないの担当訪れた構造体を入れ
  • 完了
を待つために、メインで待機グループまたはチャネルを使用します

同期を開始してから、非同期に変更するにはどうすればよいですか。同期クロール機能の前に置くだけで、同期させることができます。オリジナルのツアーを見ると、このソリューションに似ているわけではないので、これが素晴らしいモデルであるかどうかはわかりません。呼び出し元は、レースをロックまたは心配する必要はないので、デザインを変更する必要があります。 original tour exerciseからもう一度やり直したいと思います。ロックの

、Iは

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

を使用したいとTは、ロックが必要なときを決定し、データの状態を調整するかを検索する機能を有します。これにより、ロックの使用について考えるのが簡単になり、間違いを起こす可能性は低くなります。

+0

ケニーの応答に感謝します。 "同期APIを優先する" それはどういう意味ですか?私はすでに同期的にそれをやろうとしています。 "訪問先の構造体をロックする(公開ロックなし)" 私に例を教えてもらえますか? "完了待ちを待つためにメインの待ちグループを使用する" すでにすべてのgoルーチンの完了を待つチャネルを使用しています –

+0

goキーワードを使用する場合でも、非同期以外のバージョンを作成します。これは特殊なケースですが、通常、呼び出し元は非同期を呼び出すかどうかを決定します。 goキーワードで同期を非同期に変換することはできますが、同期は非同期にすることはできません。次に、呼び出し先がデータをいつロックするかを決めます。ロックする必要がある場合は、関数の外でマップを使用したり、ロック関数を公開したりしないでください。 –

関連する問題