Clojure les différences entre Ref, Var, Agent, Atom, avec des exemples

110

Je suis très nouveau sur Clojure, pouvez-vous me donner des explications avec des scénarios du monde réel. Je veux dire, où utiliser Ref, Var, Agent, Atom. J'ai lu un livre, mais je ne pouvais toujours pas comprendre les exemples du monde réel.

Mike
la source

Réponses:

174

Je recommande vivement "The Joy of Clojure" ou "programmation Clojure" pour une vraie réponse à cette question, je peux reproduire un petit aperçu des motivations pour chacun:

commencez par regarder cette vidéo sur la notion d'identité et / ou étudiez ici .

  • Les références sont pour l' accès synchrone coordonné à "plusieurs identités".
  • Les atomes sont destinés à un accès synchrone non coordonné à une seule identité.
  • Les agents sont destinés à un accès asynchrone non coordonné à une seule identité.
  • Les Vars sont pour les identités isolées locales de thread avec une valeur par défaut partagée.

L' accès coordonné est utilisé lorsque deux identités doivent changer ensemble, l'exemple classique consiste à transférer de l'argent d'un compte bancaire à un autre, il doit soit bouger complètement, soit pas du tout.

L' accès non coordonné est utilisé lorsqu'une seule identité doit être mise à jour, c'est un cas très courant.

L' accès synchrone est utilisé lorsque l'appel est censé attendre que toutes les identités soient réglées avant de continuer.

L' accès asynchrone est "feu et oublie" et laisse l'identité atteindre son nouvel état à son propre rythme.

Arthur Ulfeldt
la source
En accès coordonné, si je veux seulement changer state-a, mais que je me réfère à state-bcela, j'ai toujours besoin d'un refcorrect? Il ne s'agit donc pas de changer plusieurs choses mais de faire référence à plusieurs choses tout en changeant l'une d'entre elles?
event_jr
2
Oui, vous semblez comprendre correctement que l'état-a et l'état-b doivent tous deux être des références. Si vous voulez que la nouvelle valeur de l'état-a soit basée sur une combinaison cohérente des valeurs de a et b. Vous avez besoin que cette nouvelle valeur ait été calculée dans un contexte où l'état-a et l'état-b étaient cohérents l'un avec l'autre. Lorsqu'ils sont tous les deux refs, si b change à mi-chemin, la transaction redémarrera et utilisera les nouvelles valeurs de a et de b. pensez à utiliser la ensurefonction: clojure.github.io/clojure/clojure.core-api.html#clojure.core / ... pour rendre cela explicite et plus efficace.
Arthur Ulfeldt
3
Peut-être qu'une explication de ce que signifie Isolé avec par défaut partagé pourrait être ajoutée pour compléter la réponse?
Didier A.
1
"L'accès coordonné est utilisé lorsque deux identités doivent être modifiées ensemble ...". Cela devrait-il être «changé»?
Carcigenicate
40

Les références sont pour l'état qui doit être synchronisé entre les threads. Si vous avez besoin de suivre un tas de choses différentes et que vous aurez parfois besoin de faire des opérations qui écrivent plusieurs choses à la fois, utilisez refs. Chaque fois que vous avez plusieurs états différents, utiliser des références n'est pas une mauvaise idée.

Les atomes sont pour un état indépendant qui doit être synchronisé entre les threads. Si vous n'aurez jamais besoin de changer l'état de l'atome et quoi que ce soit d'autre en même temps, utiliser at atom est sûr (en particulier, s'il n'y a qu'un seul élément d'état dans tout le programme, vous pouvez le mettre dans un atome) . À titre d'exemple non trivial, si vous essayez de mettre en cache les valeurs de retour d'une fonction (c'est-à-dire de la mémoriser), l'utilisation d'un atome est probablement sûre - l'état est invisible pour tout ce qui se trouve en dehors de la fonction, vous n'avez donc pas à vous inquiéter à propos d'un changement d'état dans la fonction qui gâche quoi que ce soit.

Le point principal des agents est qu'ils s'exécutent dans un thread différent. Vous pouvez obtenir la valeur de l'agent et lui dire d'appliquer une fonction à sa valeur, mais vous ne savez pas quand la fonction sera exécutée ou à quelle valeur la fonction sera appliquée.

Les Vars sont pour quand vous avez besoin de stocker quelque chose sur une base par thread. Si vous avez un programme multi-thread et que chaque thread a besoin de son propre état privé, placez cet état dans une variable var.

En ce qui concerne les exemples du monde réel, si vous donnez un exemple de ce que vous essayez de faire, nous pouvons vous dire quoi utiliser.

Retief
la source
32

Lorsque j'ai lu pour la première fois sur ces types, j'ai également eu du mal à comprendre où je pouvais ou devrais utiliser chacun d'eux, alors voici ma réponse en anglais simple:

Utilisez une variable lorsque les données ne changeront pas. Cela se produit chaque fois que vous utilisez defou la plupart des fonctions commençant par deflike defn.

Utilisez un atome lorsque vous avez un seul élément qui change. Un exemple peut être un compteur ou un vecteur auquel vous souhaitez ajouter des éléments.

Utilisez une référence lorsque vous avez deux choses ou plus qui doivent changer en même temps. Pensez aux «transactions de base de données» si vous êtes familier. L'exemple canonique de ceci est le transfert d'argent d'un compte à un autre. Chaque compte pourrait être stocké dans une référence afin que des modifications puissent être apportées pour apparaître atomique.

Utilisez un agent lorsque vous voulez que quelque chose change mais que vous ne vous souciez pas du moment. Cela peut être un long calcul ou écrire quelque chose dans un fichier ou une socket. Notez qu'avec ce dernier, vous devez utiliser send-off.

Remarque: j'apprécie qu'il y ait beaucoup plus à chacun d'entre eux, mais j'espère que cela devrait vous donner un point de départ.

optevo
la source
1
Merci beaucoup pour votre réponse claire :-) Aide un débutant Clojure comme moi beaucoup.
gosukiwi
27

J'ai écrit un article avec un résumé de la différence entre eux et j'ai aidé à choisir quand utiliser lequel.

Partager l'état - quand utiliser des vars, des atomes, des agents et des refs?

J'espère que cela aidera les gens à chercher des réponses à ce sujet.

Un raccourci de l'article après la suggestion @tunaci:

Vars

Les Vars sont globales pour chaque thread.

Ne changez pas de vars après la création. C'est techniquement possible, mais c'est une mauvaise idée pour de nombreuses raisons.

Atomes

Partagez l'accès à l'état mutable pour chaque thread. Le changement se produit de manière synchrone. Réessayez lorsqu'un autre thread change l'état pendant l'exécution.

N'utilisez pas de fonctions non idempotentes et de fonctions avec une exécution de longue durée

Agents

Partagez l'accès à l'état mutable pour chaque thread. Le changement se produit de manière asynchrone.

Réfs

Refs fonctionne de manière similaire aux transactions de base de données. L'écriture et la lecture sont protégées dans dosync. Vous pouvez opérer sur de nombreuses références en toute sécurité en transaction.

Et organigramme lorsque vous utilisez lequel: organigramme

Veuillez regarder l'image sur le site Web, car certaines mises à jour sont toujours possibles.

Il est complexe et long de donner une réponse complète sans copie ni article passé, alors pardonnez-moi, je vous redirige vers le site Web :)

Kabra
la source