Les conditions de course peuvent certainement toujours exister même avec des structures de données non partagées. Considérez ce qui suit:
B asks A for the currentCount
C asks A for the currentCount
B sends A (newDataB, currentCount + 1)
A stores newDataB at location currentCount+1
C sends A (newDataC, currentCount + 1)
A stores newDataC at currentCount + 1 (overwriting newDataB; race condition)
Cette condition de course exige état mutable privé dans A, mais pas de structures de données partagées mutables et n'a même pas besoin d'état mutable en B ou C. Il n'y a rien B ou C peuvent faire pour prévenir cette condition de course sans comprendre le contrat qu'offre A.
Même Haskell peut souffrir de ce genre de conditions de concurrence dès que l'état entre dans l'équation, et l'état est très difficile à éliminer complètement d'un système réel. Finalement, vous voulez que votre programme interagisse avec la réalité, et la réalité est dynamique.Wikipedia donne a helpful race condition example in Haskell en utilisant STM. Je suis d'accord que de bonnes structures de données immuables pourraient rendre les choses plus faciles (Go n'en a pas vraiment). Les copies mutables échangent un problème pour un autre. Vous ne pouvez pas modifier accidentellement les données de quelqu'un d'autre. D'un autre côté, vous pouvez penser que vous changez le vrai, alors que vous changez simplement une copie, menant à un autre type de bogue. Vous devez comprendre le contrat de toute façon. Mais finalement, Go a tendance à suivre l'histoire de C sur la concurrence: vous créez des règles de propriété pour votre code (comme les offres @ tux21b) et assurez-vous de toujours les suivre, et si vous le faites parfaitement, ça va tout fonctionne bien, et si jamais vous faites une erreur, alors c'est évidemment votre faute, pas la langue.
(Ne vous méprenez pas, j'aime Go, beaucoup vraiment, et il offre de bons outils pour faciliter la concurrence, mais il n'offre pas beaucoup d'outils de langage pour aider à faire la simultanéité corriger. Cela dit, la réponse de tux21b offre beaucoup de bons conseils, et le détecteur de course est certainement un outil puissant pour réduire les conditions de course, il ne fait pas partie du langage, il est question de test, pas de correction, ce n'est pas le même chose.)
EDIT: À la question de savoir pourquoi les structures de données immuables rendent les choses plus faciles, c'est l'extension de votre point initial: la création d'un contrat où plusieurs parties ne changent pas le même dat une structure. Si la structure de données est immuable, alors cela vient gratuitement ...
De nombreuses langues ont un riche ensemble de collections et de classes immuables. C++ vous laisse const
à peu près tout. Objective-C possède des collections immuables avec des sous-classes mutables (ce qui crée un ensemble de modèles différent de const
). Scala a des versions mutables et immuables séparées de nombreux types de collection, et il est courant d'utiliser exclusivement les versions immuables. La déclaration de l'immutabilité dans la signature d'une méthode est une indication importante du contrat.
Lorsque vous passez un []byte
à un goroutine, il n'y a aucun moyen de savoir à partir du code si le goroutine a l'intention de modifier la tranche, ni quand vous pouvez modifier la tranche vous-même. Il y a des modèles qui émergent, mais ils sont comme la propriété d'un objet C++ avant la sémantique de déplacement; beaucoup d'approches fines, mais pas moyen de savoir lequel est utilisé. C'est une chose critique que chaque programme doit faire correctement, pourtant le langage ne vous donne pas de bons outils, et il n'y a pas de modèle universel utilisé par les développeurs.
Les conditions de course sont difficiles à déboguer, il est également difficile de les maîtriser en les évitant. Je suggère de réfléchir sur les modèles décrits ici par Rob Pike http://vimeo.com/49718712 alors qu'il passe en revue certaines constructions de la façon dont les canaux et la sémantique CSP rendent une application sûre par conception au lieu de se préoccuper de tous les problèmes associés aux mutex. Je suis désolé si cela ne répond pas à votre question mais j'espère que cela ouvrira plus de portes à de nouvelles idées. – ymg
Merci pour le lien. Oui, les conditions de course sont difficiles à empêcher, mais encore plus difficiles à déboguer! Je suppose qu'une version immuable des collections permettrait d'atténuer le problème dans une certaine mesure, mais nous sommes de retour dans la cour de la programmation fonctionnelle. – tldr
Il existe d'autres langages fonctionnels/immuables (comme F #), mais il s'agit vraiment de style. Vous avez raison d'avoir besoin de plus de mémoire, mais une conception appropriée peut éliminer certains frais généraux. Par exemple, les tableaux d'écriture sur modification peuvent être faits dans l'espace 'n log n' (ou moins pour certains cas potentiels) en moyenne, plutôt que naïf' 2n'. Bien que le support de langage/optimiseur puisse faire des choses folles pour les langages construits à cet effet ... –