web-dev-qa-db-fra.com

Langage de programmation moderne avec abstractions de programmation simultanée intuitives

Je suis intéressé à apprendre la programmation simultanée, en me concentrant sur le niveau application/utilisateur (pas sur la programmation système). Je recherche un langage de programmation moderne de haut niveau qui fournit des abstractions intuitives pour l'écriture d'applications simultanées. Je veux me concentrer sur les langages qui augmentent la productivité et masquent la complexité de la programmation simultanée.

Pour donner quelques exemples, je ne considère pas une bonne option pour écrire du code multithread en C, C++ ou Java car à mon humble avis ma productivité est réduite et leur modèle de programmation n'est pas intuitif. En revanche , les langages qui augmentent la productivité et offrent des abstractions plus intuitives tels que Python et le module de multitraitement, Erlang, Clojure, Scala, etc. seraient de bonnes options.

Que recommanderiez-vous en fonction de votre expérience et pourquoi?

EDIT: Merci à tous pour vos réponses intéressantes. Il est difficile de tirer une conclusion sans vraiment essayer car il y a beaucoup de bons candidats: Erlang, Clojure, Scala, Groovy et peut-être Haskell. J'ai voté la réponse avec les arguments les plus convaincants, mais je vais essayer tous les bons candidats avant de décider lequel choisir :)

41
sakisk

Vous devriez presque certainement regarder Clojure - à mon avis, c'est le meilleur langage moderne pour la programmation multicœur et il est extrêmement productif.

Attributs clés:

  • C'est un langage fonctionnel, ce qui est une aubaine pour la concurrence et votre capacité à développer en utilisant des abstractions de niveau supérieur. Il présente des structures de données persistantes entièrement immuables et des séquences paresseuses qui seront familières à toute personne ayant une expérience dans des langages fonctionnels comme Haskell.
  • Il dispose d'un système de mémoire transactionnelle logicielle très original pour un accès simultané sans verrouillage à un état mutable. Rendre le code sécurisé en simultané est souvent aussi simple que de l'envelopper dans un bloc (dosync ....).
  • C'est un [~ # ~] lisp [~ # ~] - ce qui le rend extrêmement puissant pour la métaprogrammation basée sur macro et la génération de code. Cela peut apporter des avantages importants productivité (essai de Paul Graham - "Beating The Averages")
  • C'est un langage JVM - non seulement vous avez accès à la vaste gamme de bibliothèques et d'outils de l'écosystème Java, vous bénéficiez également de l'énorme ingénierie efforts déployés pour faire de la JVM une plate-forme efficace pour les applications simultanées côté serveur, ce qui lui confère un avantage considérable par rapport aux langages qui ne disposent pas de ce type de base.
  • C'est dynamique - ce qui donne un code très concis et beaucoup de productivité. Notez cependant que vous pouvez utiliser des conseils de type statique facultatifs pour les performances si nécessaire.
  • Le langage est conçu autour des abstractions ce qui est quelque peu difficile à expliquer mais l'effet net est que vous obtenez un ensemble de fonctionnalités relativement orthogonales que vous pouvez combiner pour résoudre vos problèmes. Un exemple serait l'abstraction de séquence, qui vous permet d'écrire du code qui traite de chaque type d'objet "séquentiel" (qui comprend tout, des listes, chaînes, Java, séquences paresseuses infinies, lignes être lu à partir d'un fichier, etc.)
  • Il y a une grande communauté - utile, perspicace mais surtout très pragmatique - l'accent dans Clojure est généralement sur "faire avancer les choses".

Quelques exemples de mini code avec une inclinaison de concurrence:

;; define and launch a future to execute do-something in another thread
(def a (future (do-something)))

;; wait for the future to finish and print its return value
(println @a)

;; call two functions protected in a single STM transaction
(dosync
  (function-one)
  (function-two))

En particulier, cela vaut la peine de regarder une ou plusieurs de ces vidéos:

33
mikera

Vous pouvez essayer D. Il propose trois modèles. Je recommande le premier ou le second.

  1. std.concurrency . Si vous utilisez ce module pour tous vos besoins de simultanéité, une combinaison de la langue et de la bibliothèque standard applique l'isolement entre les threads. Les threads communiquent principalement via le passage de messages, avec une prise en charge limitée de la mémoire partagée d'une manière qui favorise la "sécurité d'abord" et interdit les courses de données de bas niveau. Malheureusement, la documentation de std.concurrency doit être améliorée, mais le modèle est documenté dans un chapitre gratuit du livre d'Andrei Alexandrescu, "The D Programming Language".

  2. parallélisme std . Ce module est conçu spécifiquement pour le parallélisme multicœur plutôt que pour la concurrence générale. ( La simultanéité et le parallélisme ne sont pas la même chose, bien que la simultanéité soit nécessaire pour implémenter le parallélisme. ) Puisque tout le point du parallélisme est la performance, std.parallelism ne fait aucune garantie d'isolement car ils rendraient l'écriture code parallèle efficace difficile. Cependant, il résume beaucoup de détails de bas niveau sujets aux erreurs, de sorte qu'il est très difficile de bousiller si vous parallélisez des charges de travail dont vous avez vérifié manuellement qu'elles sont mutuellement indépendantes.

  3. core.thread est un wrapper de bas niveau sur les API de threading spécifiques au système d'exploitation. Std.concurrency et std.parallelism l'utilisent sous le capot, mais je ne recommanderais de l'utiliser que si vous écrivez votre propre bibliothèque de concurrence ou si vous trouvez un cas d'angle ridicule qui ne peut pas être bien fait dans std.parallelism ou std .concurrency. Personne ne devrait utiliser quelque chose d'aussi bas pour le travail quotidien.

27
dsimcha

Jetez un oeil à Programmation parallèle de Microsoft pour .net. Il est très intuitif.

De nombreux ordinateurs personnels et postes de travail ont deux ou quatre cœurs (c'est-à-dire des processeurs) qui permettent d'exécuter simultanément plusieurs threads. Dans un avenir proche, les ordinateurs devraient avoir beaucoup plus de cœurs. Pour tirer parti du matériel d'aujourd'hui et de demain, vous pouvez paralléliser votre code pour répartir le travail sur plusieurs processeurs. Dans le passé, la parallélisation nécessitait une manipulation de bas niveau des threads et des verrous. Visual Studio 2010 et .NET Framework 4 améliorent la prise en charge de la programmation parallèle en fournissant un nouveau runtime, de nouveaux types de bibliothèques de classes et de nouveaux outils de diagnostic. Ces fonctionnalités simplifient le développement parallèle afin que vous puissiez écrire du code parallèle efficace, fin et évolutif dans un idiome naturel sans avoir à travailler directement avec les threads ou le pool de threads. L'illustration suivante fournit une vue d'ensemble de l'architecture de programmation parallèle dans .NET Framework 4 ... http://i.msdn.Microsoft.com/dynimg/IC292903.png

23
Otávio Décio

Erlang est certainement une excellente option, mais quelque chose d'un peu plus pratique pourrait être Go , la nouvelle langue de Google.

Ce n'est pas si loin des autres langues courantes, il est donc généralement facile à obtenir si vous connaissez déjà d'autres langues "faciles". Beaucoup de gens le comparent avec Python ou même Lua en termes de "confort" de programmation.

23
Javier

Erlang et Scala ont concurrence basée sur l'acteur , que j'ai trouvé très intuitif et facile à apprendre.

Le modèle Acteur en informatique est un modèle mathématique de calcul simultané qui traite les "acteurs" comme les primitives universelles du calcul numérique simultané: en réponse à un message qu'il reçoit, un acteur peut prendre des décisions locales, créer plus d'acteurs, envoyer plus de messages , et déterminer comment répondre au prochain message reçu ... Il a été utilisé à la fois comme cadre pour une compréhension théorique du calcul, et comme base théorique pour plusieurs implémentations pratiques de systèmes concurrents.

21
TMN

J'apprends Haskell en ce moment et lire cet article m'a convaincu que Haskell est une bonne option pour la programmation simultanée. Parce qu'elle est purement fonctionnelle (le système de type sait si une fonction fait une entrée, une sortie ou une lecture/modification de l'état global), elle peut faire des choses comme la mémoire transactionnelle logicielle (résumée très bien dans le document ci-dessus) qui se comporte de manière similaire aux transactions dans les bases de données - vous obtenez un tas de belles choses comme l'atomicité avec seulement un peu de sucre supplémentaire. AFAIK, les fils Haskell sont également très légers. En plus de ces choses, le fait que Haskell soit purement fonctionnel permet d'exécuter même des tâches simples en parallèle avec un peu plus qu'un seul mot-clé (par). source

19
user41310

Le langage GO de Google possède des outils intéressants pour la concurrence - ce serait une autre chose amusante à essayer. Voir: http://golang.org/doc/effective_go.html#concurrency et lire un peu pour des exemples.

La programmation simultanée est un sujet important et il n'y a de l'espace que pour certains points forts spécifiques à Go ici.

La programmation simultanée dans de nombreux environnements est rendue difficile par les subtilités requises pour implémenter un accès correct aux variables partagées. Go encourage une approche différente dans laquelle les valeurs partagées sont transmises sur les canaux et, en fait, jamais activement partagées par des threads d'exécution distincts. Un seul goroutine a accès à la valeur à un moment donné. Les courses de données ne peuvent pas se produire, par conception. Pour encourager cette façon de penser, nous l'avons réduite à un slogan:

Ne communiquez pas en partageant la mémoire; au lieu de cela, partagez la mémoire en communiquant.

Cette approche peut être poussée trop loin. Le comptage des références peut être mieux fait en plaçant un mutex autour d'une variable entière, par exemple. Mais en tant qu'approche de haut niveau, l'utilisation de canaux pour contrôler l'accès facilite l'écriture de programmes clairs et corrects.

Une façon de penser à ce modèle consiste à envisager un programme monothread typique s'exécutant sur un processeur. Il n'a pas besoin de primitives de synchronisation. Exécutez maintenant une autre instance de ce type; il n'a pas besoin non plus de synchronisation. Maintenant, laissez ces deux-là communiquer; si la communication est le synchroniseur, il n'y a toujours pas besoin d'une autre synchronisation. Les pipelines Unix, par exemple, conviennent parfaitement à ce modèle. Bien que l'approche de Go en matière de concurrence trouve son origine dans les processus de communication séquentiels (CSP) de Hoare, elle peut également être considérée comme une généralisation sécurisée des canaux Unix ...

7
Aerik

Dans la prochaine version, C # le rend encore plus facile que ce que le diagramme montre. Il y a deux nouveaux mots clés Async et Await.

Async est utilisé comme modificateur de fonction et dit "cette opération effectue son travail sur un autre thread.

Await est utilisé dans une fonction Async, et c'est là que la magie opère. Fondamentalement, Await indique au compilateur d'exécuter l'opération en suivant le mot-clé dans un thread séparé et d'attendre les résultats. Tout code après l'appel en attente s'exécute après l'opération.

ÉGALEMENT, l'opération se synchronise avec le thread appelant (donc si vous effectuez une opération asynchrone en réponse à un clic sur un bouton, vous n'avez pas besoin de publier manuellement de nouveau dans le thread d'interface utilisateur). Deux petits mots-clés et vous obtenez beaucoup de pouvoir de concurrence. En savoir plus ici

6
Michael Brown

Un plug pour Ada est nécessaire ici, car il a toutes les abstractions de niveau supérieur pour le parallélisme et la concurrence. autrement connu sous le nom de tâches . Aussi, comme OP l'a demandé intuitif (un critère subjectif!), Je pense qu'une approche différente du monde centré sur Java pourrait être appréciée.

6
NWS

Je recommanderais toujours C++. Il est plus que capable des abstractions nécessaires pour écrire du code concurrent décent. La probabilité écrasante est que vous avez simplement une bibliothèque médiocre pour faire le travail, car bonne les bibliothèques pour faire le travail sont relativement nouvelles, et en effet, les connaissances pour bien utiliser C++ ne sont pas exactement communes. Le TBB d'Intel n'existe que depuis quelques années et Microsoft PPL n'a été expédié que l'an dernier.

Si vous utilisez quelque chose comme TBB ou PPL, alors le code concurrent n'est pas exactement trivial à écrire, dans la mesure où la concurrence n'est jamais triviale, mais loin d'être ardue. Si vous utilisez directement des threads pthreads ou Win32, il n'est pas étonnant que vous n'aimiez pas cela - vous écrivez pratiquement en assembleur avec de telles fonctions. Mais avec le PPL, alors vous parlez d'algorithmes fonctionnels standard qui sont parallélisés pour vous, de structures de données génériques pour un accès simultané, et ce genre de bonnes choses.

6
DeadMG

Je suggérerais Groovy /Java/ GPars si vous pouvez être basé sur JVM car il permet les acteurs, le flux de données, les processus séquentiels communicants (CSP), le parallélisme des données, la mémoire transactionnelle logicielle ( STM), les agents, ... Le point ici est qu'il existe de nombreux modèles de parallélisme et de parallélisme de haut niveau dont chacun a des "points faibles" différents. Vous ne voulez pas utiliser un modèle qui n'est pas en harmonie avec la solution à un problème que vous essayez de construire. Les langages et les frameworks avec un seul modèle vous contraignent au piratage d'algorithmes.

Bien sûr, je pourrais être considéré comme biaisé car je contribue à Groovy et GPars. Par contre je travaille avec CSP et Python, cf. Python-CSP.

Un autre point est que la question initiale concerne l'apprentissage et non l'écriture d'un système de production. Ainsi, la combinaison Groovy/Java/GPars est un bon moyen d'apprendre même si le travail de production éventuel est effectué en C++ en utilisant quelque chose comme Just :: Thread Pro ou TBB plutôt que d'être basé sur JVM.

(Certains liens URL parfaitement raisonnables ont dû être supprimés en raison de la panique suscitée par le spam par le site hôte.)

5
Russel Winder

Et Clojure? Vous pouvez utiliser Swing par exemple, mais profiter de la fonction de programmation simultanée de Clojure? Clojure a une assez bonne intégration Java.

Avez-vous également pris en compte framework Java 7 Fork/Join ?

4
Chiron

Vous pouvez également consulter Groovy et la bibliothèque GPars . GPars BTW est quelque peu similaire à .NET Parallel Extension mentionné dans une autre réponse, mais la syntaxe flexible de Groovys facilite sa lecture dans certaines circonstances.

2
Christian Horsdal

Scala a été mentionné à plusieurs reprises dans les questions et réponses, mais je n'ai vu aucune référence à Akka qui est une implémentation d'acteur qui peut être utilisée avec les deux Scala et Java.

0
Giorgio