Cet article vous présente principalement les informations pertinentes sur les problèmes de boucle for et de goroutine dans Golang. L'article les présente en détail à travers un exemple de code. Il a une certaine valeur d'apprentissage de référence pour que tous puissent apprendre ou utiliser Golang. Apprenons avec l'éditeur ci-dessous.
Contexte
Récemment, alors que j'étudiais le cours distribué 6.824 du MIT, j'ai rencontré quelques problèmes lors de l'utilisation de Go pour implémenter le protocole Raft. question. Je le partage pour référence et étude de tous. Je n’en dirai pas plus ci-dessous, jetons un coup d’œil à l’introduction détaillée.
Voir le code suivant :
for i := 0; i < len(rf.peers); i++ { DPrintf("i = %d", i) if i == rf.me { DPrintf("skipping myself #%d", rf.me) continue } go func() { DPrintf("len of rf.peers = %d", len(rf.peers)) DPrintf("server #%d sending request vote to server %d", rf.me, i) reply := &RequestVoteReply{} ok := rf.sendRequestVote(i, args, reply) if ok && reply.VoteGranted && reply.Term == rf.currentTerm { rf.voteCount++ if rf.voteCount > len(rf.peers)/2 { rf.winElectionCh <- true } } }() }
Parmi eux, la longueur de la tranche des pairs est de 3, donc la plus élevée l'indice est 2, la boucle for dans le code devrait être très intuitive dans la programmation non parallèle, et je n'avais pas réalisé qu'il y avait quelque chose qui n'allait pas à l'époque. Cependant, pendant le processus de débogage, des erreurs d'index hors limites ont continué à être signalées. Les informations de débogage montraient que la valeur de i était 3. À ce moment-là, je n'arrivais toujours pas à comprendre comment la condition de boucle pouvait devenir 3 alors qu'elle était clairement i <
Analyse
Bien que je ne comprenne pas ce qui s'est passé, je sais que cela devrait être causé par la goroutine introduite dans le boucle. Après Google, j'ai découvert qu'il y avait une page dans le wiki de Go intitulée Common Mistake - Using goroutines on loop iterator variables qui mentionne spécifiquement ce problème. Cela semble être très courant ~
Les débutants l'utilisent souvent. Utilisez le code suivant pour traiter les données en parallèle :
for val := range values { go val.MyMethod() }
ou utilisez la fermeture :
for val := range values { go func() { fmt.Println(val) }() }
Le problème ici est que val est en fait une variable unique qui parcourt toutes les données de la tranche. Puisque la fermeture n'est liée qu'à cette variable val, il est très probable que le résultat du code ci-dessus soit que toutes les goroutines génèrent le dernier élément de la tranche. En effet, il est très probable que la goroutine ne commencera à s'exécuter qu'après l'exécution de la boucle for, et à ce moment-là, la valeur de val pointe vers le dernier élément de la tranche.
The val variable in the above loops is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.
Solution
La bonne façon d'écrire le code ci-dessus est :
for val := range values { go func(val interface{}) { fmt.Println(val) }(val) }
Ici, val est transmis à la goroutine en tant que paramètre. Chaque val sera calculée indépendamment et enregistrée dans la pile de la goroutine, obtenant ainsi les résultats attendus.
Une autre méthode consiste à définir de nouvelles variables dans la boucle. Étant donné que les variables définies dans la boucle ne sont pas partagées lors du parcours de la boucle, le même effet peut être obtenu :
<🎜. >
for i := range valslice { val := valslice[i] go func() { fmt.Println(val) }() }
server := i
Résumé
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!