J'observe la visibilité croissante des langages de programmation fonctionnels et des fonctionnalités depuis un certain temps. Je les ai examinés et je n'ai pas vu la raison de l'appel.
Puis, récemment, j'ai assisté à la présentation "Basics of Erlang" de Kevin Smith à Codemash .
J'ai apprécié la présentation et j'ai appris que de nombreux attributs de la programmation fonctionnelle permettent d'éviter les problèmes de thread / concurrence. Je comprends que le manque d'état et la mutabilité empêchent plusieurs threads de modifier les mêmes données, mais Kevin a déclaré (si j'ai bien compris) que toutes les communications se font par le biais de messages et que les messages sont traités de manière synchrone (évitant encore une fois les problèmes de concurrence).
Mais j'ai lu qu'Erlang est utilisé dans des applications hautement évolutives (la raison pour laquelle Ericsson l'a créé en premier lieu). Comment gérer efficacement des milliers de requêtes par seconde si tout est traité comme un message traité de manière synchrone? N'est-ce pas la raison pour laquelle nous avons commencé à évoluer vers le traitement asynchrone - afin que nous puissions profiter de l'exécution de plusieurs threads d'opération en même temps et atteindre l'évolutivité? Il semble que cette architecture, bien que plus sûre, soit un pas en arrière en termes d'évolutivité. Qu'est-ce que je rate?
Je comprends que les créateurs d'Erlang ont délibérément évité de prendre en charge le threading pour éviter les problèmes de concurrence, mais je pensais que le multi-threading était nécessaire pour atteindre l'évolutivité.
Comment les langages de programmation fonctionnels peuvent-ils être intrinsèquement thread-safe, tout en restant évolutifs?
la source
Réponses:
Un langage fonctionnel ne repose pas (en général) sur la mutation d' une variable. Pour cette raison, nous n'avons pas à protéger «l'état partagé» d'une variable, car la valeur est fixe. Cela évite à son tour la majorité des sauts en cerceau que les langages traditionnels doivent traverser pour implémenter un algorithme sur des processeurs ou des machines.
Erlang va plus loin que les langages fonctionnels traditionnels en intégrant un système de passage de messages qui permet à tout de fonctionner sur un système basé sur des événements où un morceau de code ne se soucie que de recevoir des messages et d'envoyer des messages, sans se soucier d'une image plus grande.
Cela signifie que le programmeur ne se soucie (nominalement) pas que le message soit traité sur un autre processeur ou une autre machine: le simple fait d'envoyer le message suffit pour qu'il continue. S'il se soucie d'une réponse, il l'attendra comme un autre message .
Le résultat final est que chaque extrait de code est indépendant de tous les autres extraits. Pas de code partagé, pas d'état partagé et toutes les interactions provenant d'un système de messagerie pouvant être réparti entre de nombreux matériels (ou non).
Comparez cela avec un système traditionnel: nous devons placer des mutex et des sémaphores autour des variables «protégées» et de l'exécution du code. Nous avons une liaison serrée dans un appel de fonction via la pile (en attente du retour). Tout cela crée des goulots d'étranglement qui sont moins problématiques dans un système de partage du rien comme Erlang.
EDIT: Je dois également souligner qu'Erlang est asynchrone. Vous envoyez votre message et peut-être qu'un jour un autre message revient. Ou pas.
Le point de Spencer sur l'exécution dans le désordre est également important et bien répondu.
la source
Le système de file d'attente de messages est cool car il produit effectivement un effet "feu et attente de résultat" qui est la partie synchrone que vous lisez. Ce qui rend cela incroyablement génial, c'est que cela signifie que les lignes n'ont pas besoin d'être exécutées séquentiellement. Considérez le code suivant:
Considérez un instant que methodWithALotOfDiskProcessing () prend environ 2 secondes pour se terminer et que methodWithALotOfNetworkProcessing () prend environ 1 seconde pour se terminer. Dans un langage procédural, ce code prendrait environ 3 secondes à s'exécuter car les lignes seraient exécutées séquentiellement. Nous perdons du temps à attendre la fin d'une méthode qui pourrait s'exécuter simultanément avec l'autre sans rivaliser pour une seule ressource. Dans un langage fonctionnel, les lignes de code ne dictent pas quand le processeur les tentera. Un langage fonctionnel essaierait quelque chose comme ce qui suit:
À quel point cela est cool? En continuant avec le code et en n'attendant que si nécessaire, nous avons réduit le temps d'attente à deux secondes automatiquement! : D Donc oui, bien que le code soit synchrone, il a tendance à avoir une signification différente de celle des langages procéduraux.
ÉDITER:
Une fois que vous avez compris ce concept en conjonction avec l'article de Godeke, il est facile d'imaginer à quel point il devient simple de tirer parti de plusieurs processeurs, de fermes de serveurs, de magasins de données redondants et qui sait quoi d'autre.
la source
Il est probable que vous mélangiez synchrone avec séquentiel .
Le corps d'une fonction dans erlang est traité séquentiellement. Donc ce que Spencer a dit à propos de cet "effet automagique" ne vaut pas pour erlang. Vous pouvez cependant modéliser ce comportement avec erlang.
Par exemple, vous pouvez générer un processus qui calcule le nombre de mots dans une ligne. Comme nous avons plusieurs lignes, nous engendrons un tel processus pour chaque ligne et recevons les réponses pour en calculer une somme.
De cette façon, nous engendrons des processus qui effectuent les calculs «lourds» (en utilisant des cœurs supplémentaires si disponibles) et plus tard nous collectons les résultats.
Et voici à quoi cela ressemble, lorsque nous exécutons ceci dans le shell:
la source
L'élément clé qui permet à Erlang d'évoluer est lié à la concurrence.
Un système d'exploitation fournit la concurrence par deux mécanismes:
Les processus ne partagent pas l'état - un processus ne peut pas planter un autre par conception.
L'état de partage des threads - un thread peut en planter un autre par conception - c'est votre problème.
Avec Erlang - un processus du système d'exploitation est utilisé par la machine virtuelle et la VM fournit la concurrence au programme Erlang non pas en utilisant les threads du système d'exploitation mais en fournissant des processus Erlang - c'est qu'Erlang implémente son propre timelicer.
Ces processus Erlang se parlent en envoyant des messages (gérés par la VM Erlang et non par le système d'exploitation). Les processus Erlang s'adressent les uns aux autres à l'aide d'un ID de processus (PID) qui a une adresse en trois parties
<<N3.N2.N1>>
:Deux processus sur la même VM, sur des VM différentes sur la même machine ou deux machines communiquent de la même manière - votre mise à l'échelle est donc indépendante du nombre de machines physiques sur lesquelles vous déployez votre application (en première approximation).
Erlang n'est threadsafe que dans un sens trivial - il n'a pas de threads. (Le langage dans lequel la machine virtuelle SMP / multicœur utilise un thread de système d'exploitation par cœur).
la source
Vous avez peut-être un malentendu sur le fonctionnement d'Erlang. Le runtime Erlang minimise le changement de contexte sur un CPU, mais s'il y a plusieurs CPU disponibles, alors tous sont utilisés pour traiter les messages. Vous n'avez pas de "threads" dans le sens où vous le faites dans d'autres langues, mais vous pouvez avoir un grand nombre de messages traités simultanément.
la source
Les messages Erlang sont purement asynchrones, si vous voulez une réponse synchrone à votre message, vous devez coder explicitement pour cela. Ce qui a peut-être été dit, c'est que les messages dans une boîte de message de processus sont traités séquentiellement. Tout message envoyé à un processus se trouve dans cette boîte de message de processus, et le processus peut choisir un message dans cette boîte, le traiter, puis passer au suivant, dans l'ordre qu'il juge approprié. C'est un acte très séquentiel et le bloc de réception fait exactement cela.
On dirait que vous avez mélangé synchrone et séquentiel, comme Chris l'a mentionné.
la source
Transparence référentielle: voir http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)
la source
Dans un langage purement fonctionnel, l'ordre d'évaluation n'a pas d'importance - dans une application de fonction fn (arg1, .. argn), les n arguments peuvent être évalués en parallèle. Cela garantit un haut niveau de parallélisme (automatique).
Erlang utilise un modèle de processus dans lequel un processus peut s'exécuter dans la même machine virtuelle ou sur un processeur différent - il n'y a aucun moyen de le dire. Cela n'est possible que parce que les messages sont copiés entre les processus, il n'y a pas d'état partagé (mutable). Le paralellisme multiprocesseur va beaucoup plus loin que le multi-threading, puisque les threads dépendent de la mémoire partagée, il ne peut y avoir que 8 threads fonctionnant en parallèle sur un processeur à 8 cœurs, tandis que le multi-traitement peut évoluer vers des milliers de processus parallèles.
la source