2017-10-14 11 views
0

J'ai suivi ce solution. Il n'y a aucune condition de course détectée quand je cours le detecter de course. Mais quand je lance detecter course avec mon code, il donne l'erreur suivante:Golang Web Solution de chenille, 2 courses de données, statut de sortie 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

Après mon code, quelqu'un peut-il s'il vous plaît me dire où je vais mal. J'ai essayé de le comprendre depuis si longtemps mais je ne pouvais pas l'identifier.

 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

Pourriez-vous montrer le fichier complet? Peut-être sur play.google.com ou github? Maintenant, ce n'est pas évident où sont ces lignes avec la panique de course. –

+0

C'est probablement l'utilisation de 'nestedUrl' dans l'appel' printf' de l'enfant goroutine. –

+0

si vous ne partagez pas le fichier entier, nous ne pouvons pas savoir quelle ligne échoue –

Répondre

0

Vous appelez Crawl, qui se déclenche au large d'une routine d'aller à récursif, vous avez accès à la carte protégée visité sans mutex principal qui est exécutée avant une crawls finition. Quelques points sur le style:

  • Préférez un api synchrone
  • Mettre le struct visité en charge de verrouillage (sans serrure publique)
  • Utilisez un groupe d'attente ou canaux principal pour attendre la fin

Commencez donc de manière synchrone, puis déterminez la meilleure façon de passer en mode asynchrone. Vous pouvez ensuite faire une synchronisation juste en mettant en avant votre fonction d'analyse synchrone. En regardant la tournée originale, ça ne ressemble pas beaucoup à cette solution, donc je ne suis pas sûr que ce soit un bon modèle à suivre. L'appelant ne devrait pas avoir à se verrouiller ou à s'inquiéter des courses, vous devez donc le repenser. Je recommencerais à partir du original tour exercise.

Pour la serrure, j'utiliser

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

et le T décide lors du verrouillage est nécessaire et a des fonctions pour régler l'état des données ou recherchez. Cela rend plus facile de penser à l'utilisation de la serrure et moins vous faites une erreur.

+0

Merci pour la réponse Kenny. "Préférer une API synchrone" Qu'est-ce que cela signifie? Je veux dire que j'essaie déjà de le faire de manière synchrone "Mettre la structure visitée en charge du verrouillage (pas de verrou public)" Pouvez-vous s'il vous plaît me montrer un exemple? "Utilisez un groupe d'attente ou des canaux dans main pour attendre la fin" Vous utilisez déjà un canal pour attendre la fin de toutes les routines go –

+0

Même si vous avez l'intention d'utiliser le mot-clé go, commencez par écrire une version non asynchrone. C'est un cas particulier, mais normalement l'appelant décide d'appeler async ou non. Vous pouvez convertir la synchronisation en async avec le mot-clé go, mais jamais en synchrone asynchrone. Ensuite, l'appelé décide quand verrouiller ses données, si vous avez besoin de verrouiller, n'utilisez pas la carte en dehors de la fonction ou exposer les fonctions de verrouillage. –