Java et C # assurent la sécurité de la mémoire en vérifiant les limites des tableaux et les déréférences des pointeurs.
Quels mécanismes pourraient être mis en œuvre dans un langage de programmation pour éviter la possibilité de conditions de concurrence et de blocages?
Réponses:
Les courses se produisent lorsque vous avez un alias simultané d'un objet et qu'au moins l'un des alias est en mutation.
Donc, pour empêcher les courses, vous devez rendre une ou plusieurs de ces conditions fausses.
Différentes approches abordent différents aspects. La programmation fonctionnelle met l'accent sur l'immuabilité qui supprime la mutabilité. Le verrouillage / atomique supprime la simultanéité. Les types affines suppriment l'aliasing (Rust supprime l'aliasing mutable). Les modèles d'acteurs suppriment généralement l'aliasing.
Vous pouvez restreindre les objets pouvant être aliasés afin qu'il soit plus facile de garantir que les conditions ci-dessus sont évitées. C'est là qu'interviennent les canaux et / ou les styles de passage de messages. Vous ne pouvez pas alias de mémoire arbitraire, juste la fin d'un canal ou d'une file d'attente qui est arrangé pour être sans course. Habituellement, en évitant la simultanéité, c'est-à-dire les verrous ou atomiques.
L'inconvénient de ces différents mécanismes est qu'ils restreignent les programmes que vous pouvez écrire. Plus la restriction est brutale, moins les programmes sont nombreux. Il n'y a donc pas d'alias ou de mutabilité, et ils sont faciles à raisonner, mais sont très limitatifs.
C'est pourquoi la rouille fait autant de bruit. C'est un langage d'ingénierie (par opposition à un langage académique) qui prend en charge l'aliasing et la mutabilité, mais le compilateur vérifie qu'ils ne se produisent pas simultanément. Bien que n'étant pas l'idéal, il permet d'écrire en toute sécurité une plus grande classe de programmes que beaucoup de ses prédécesseurs.
la source
Il est important de réfléchir d'abord à la manière dont C # et Java procèdent. Ils le font en convertissant ce qui est un comportement indéfini en C ou C ++ en comportement défini: planter le programme . Les déréférences nulles et les exceptions d'index de tableau ne doivent jamais être interceptées dans un programme C # ou Java correct; ils ne devraient pas se produire en premier lieu parce que le programme ne devrait pas avoir ce bogue.
Mais je pense que ce n'est pas ce que vous entendez par votre question! Nous pourrions assez facilement écrire un runtime "deadlock safe" qui vérifie périodiquement s'il y a n threads qui s'attendent mutuellement et terminent le programme si cela se produit, mais je ne pense pas que cela vous satisfasse.
Le problème suivant auquel nous sommes confrontés avec votre question est que les "conditions de concurrence", contrairement aux blocages, sont difficiles à détecter. N'oubliez pas que ce que nous recherchons en matière de sécurité des threads n'est pas d' éliminer les courses . Ce que nous voulons, c'est que le programme soit correct, peu importe qui remporte la course ! Le problème avec les conditions de concurrence n'est pas que deux threads s'exécutent dans un ordre indéfini et nous ne savons pas qui va terminer en premier. Le problème avec les conditions de concurrence est que les développeurs oublient que certaines commandes de finition de threads sont possibles et ne tiennent pas compte de cette possibilité.
Donc, votre question se résume essentiellement à "y a-t-il un moyen pour un langage de programmation de s'assurer que mon programme est correct?" et la réponse à cette question est, en pratique, non.
Jusqu'à présent, je n'ai fait que critiquer votre question. Permettez-moi d'essayer de changer de vitesse ici et de répondre à l'esprit de votre question. Existe-t-il des choix que les concepteurs de langage pourraient faire pour atténuer la situation horrible dans laquelle nous nous trouvons avec le multithreading?
La situation est vraiment horrible! Obtenir un code multithread correct, en particulier sur des architectures de modèle de mémoire faible, est très, très difficile. Il est instructif de réfléchir aux raisons de la difficulté:
Il existe donc un moyen évident pour les concepteurs de langage d'améliorer les choses. Abandonnez les gains de performances des processeurs modernes . Assurez-vous que tous les programmes, même ceux à plusieurs threads, ont un modèle de mémoire extrêmement solide. Cela rendra les programmes multithread beaucoup, beaucoup plus lents, ce qui va directement à l'encontre de la raison d'avoir des programmes multithread en premier lieu: pour de meilleures performances.
Même en laissant de côté le modèle de mémoire, il existe d'autres raisons pour lesquelles le multithreading est difficile:
Ce dernier point mérite une explication supplémentaire. Par "composable", j'entends ce qui suit:
Supposons que nous souhaitons calculer un int donné un double. Nous écrivons une implémentation correcte du calcul:
Supposons que nous souhaitons calculer une chaîne avec un int:
Maintenant, si nous voulons calculer une chaîne avec un double:
G et F peuvent être composés en une solution correcte au problème plus complexe.
Mais les serrures n'ont pas cette propriété à cause des blocages. Une méthode correcte M1 qui prend des verrous dans l'ordre L1, L2 et une méthode correcte M2 qui prend des verrous dans l'ordre L2, L1, ne peuvent pas toutes les deux être utilisées dans le même programme sans créer un programme incorrect. Les verrous font en sorte que vous ne pouvez pas dire "chaque méthode individuelle est correcte, donc tout est correct".
Alors, que pouvons-nous faire, en tant que concepteurs linguistiques?
D'abord, n'y allez pas. Plusieurs threads de contrôle dans un programme sont une mauvaise idée, et le partage de mémoire entre les threads est une mauvaise idée, donc ne le mettez pas dans la langue ou le runtime en premier lieu.
Il s'agit apparemment d'un non-démarreur.
Tournons ensuite notre attention vers la question la plus fondamentale: pourquoi avons-nous en premier lieu plusieurs fils? Il y a deux raisons principales, et elles sont souvent confondues dans la même chose, bien qu'elles soient très différentes. Ils sont confondus car ils concernent tous deux la gestion de la latence.
Mauvaise idée. Au lieu de cela, utilisez une asynchronie à thread unique via les coroutines. C # le fait magnifiquement. Java, pas si bien. Mais c'est le principal moyen que la génération actuelle de concepteurs de langage aide à résoudre le problème de threading. L'
await
opérateur en C # (inspiré des workflows asynchrones F # et autres antériorités) est en cours d'intégration dans de plus en plus de langages.Les concepteurs de langage peuvent vous aider en créant des fonctionnalités de langage qui fonctionnent bien avec le parallélisme. Pensez à la façon dont LINQ est étendu si naturellement à PLINQ, par exemple. Si vous êtes une personne sensée et que vous limitez vos opérations TPL à des opérations liées au processeur qui sont très parallèles et ne partagent pas la mémoire, vous pouvez obtenir de gros gains ici.
Que pouvons-nous faire d'autre?
C # ne vous permet pas d'attendre dans une serrure, car c'est une recette pour les blocages. C # ne vous permet pas de verrouiller un type de valeur car c'est toujours la mauvaise chose à faire; vous verrouillez la boîte, pas la valeur. C # vous avertit si vous alias un volatile, car l'alias n'impose pas de sémantique d'acquisition / libération. Il existe de nombreuses autres façons pour le compilateur de détecter les problèmes courants et de les éviter.
C # et Java ont fait une énorme erreur de conception en vous permettant d'utiliser n'importe quel objet de référence comme moniteur. Cela encourage toutes sortes de mauvaises pratiques qui rendent plus difficile le repérage des blocages et plus difficile à éviter statiquement. Et cela gaspille des octets dans chaque en-tête d'objet. Les moniteurs doivent provenir d'une classe de moniteurs.
STM est une belle idée, et j'ai joué avec des implémentations de jouets dans Haskell; il vous permet de composer de manière beaucoup plus élégante des solutions correctes à partir de pièces correctes que les solutions basées sur les verrous. Cependant, je ne connais pas suffisamment les détails pour expliquer pourquoi il n'a pas été possible de travailler à grande échelle; demandez à Joe Duffy la prochaine fois que vous le verrez.
Il y a eu beaucoup de recherches sur les langages basés sur le calcul de processus et je ne comprends pas très bien cet espace; essayez de lire vous-même quelques articles et voyez si vous avez des idées.
Après avoir travaillé chez Microsoft sur Roslyn, j'ai travaillé chez Coverity, et l'une des choses que j'ai faites a été d'obtenir l'interface utilisateur de l'analyseur en utilisant Roslyn. En ayant une analyse lexicale, syntaxique et sémantique précise fournie par Microsoft, nous pourrions alors nous concentrer sur le dur travail d'écriture des détecteurs qui ont trouvé des problèmes communs de multithreading.
Une raison fondamentale pour laquelle nous avons des races et des blocages et tout ça, c'est parce que nous écrivons des programmes qui disent quoi faire , et il se trouve que nous sommes tous des conneries à écrire des programmes impératifs; l'ordinateur fait ce que vous lui dites et nous lui demandons de faire les mauvaises choses. De nombreux langages de programmation modernes sont de plus en plus sur la programmation déclarative: dites quels résultats vous voulez, et laissez le compilateur trouver la manière efficace, sûre et correcte pour atteindre ce résultat. Encore une fois, pensez à LINQ; nous voulons que vous disiez
from c in customers select c.FirstName
, qui exprime une intention . Laissez le compilateur comprendre comment écrire le code.Les algorithmes d'apprentissage automatique sont bien meilleurs pour certaines tâches que les algorithmes codés à la main, bien qu'il y ait bien sûr de nombreux compromis, notamment la correction, le temps de formation, les biais introduits par une mauvaise formation, etc. Mais il est probable qu'un grand nombre de tâches que nous codons actuellement "à la main" pourront bientôt faire l'objet de solutions générées par machine. Si les humains n'écrivent pas le code, ils n'écrivent pas de bogues.
Désolé, c'était un peu décousu; c'est un sujet énorme et difficile et aucun consensus clair n'a émergé dans la communauté PL au cours des 20 années que j'ai suivies les progrès dans cet espace problématique.
la source