Clojure Unterschiede zwischen Ref, Var, Agent, Atom, mit Beispielen

110

Ich bin sehr neu in Clojure. Könnt ihr mir Erklärungen mit realen Szenarien geben? Ich meine, wo man Ref, Var, Agent, Atom benutzt. Ich las ein Buch, konnte aber die Beispiele der realen Welt immer noch nicht verstehen.

Mike
quelle

Antworten:

174

Ich empfehle "The Joy of Clojure" oder "Programming Clojure" für eine echte Antwort auf diese Frage. Ich kann einen kurzen Ausschnitt der Motivationen für jeden reproduzieren:

Sehen Sie sich zunächst dieses Video zum Thema Identität an und / oder studieren Sie hier .

  • Refs sind für den koordinierten synchronen Zugriff auf "Viele Identitäten".
  • Atome sind für den unkoordinierten synchronen Zugriff auf eine einzelne Identität vorgesehen.
  • Agenten sind für den unkoordinierten asynchronen Zugriff auf eine einzelne Identität vorgesehen.
  • Vars sind für lokal isolierte Thread- Identitäten mit einem gemeinsamen Standardwert.

Der koordinierte Zugriff wird verwendet, wenn zwei Identitäten gemeinsam geändert werden müssen. Das klassische Beispiel ist das Verschieben von Geld von einem Bankkonto auf ein anderes. Es muss entweder vollständig oder gar nicht verschoben werden.

Der unkoordinierte Zugriff wird verwendet, wenn nur eine Identität aktualisiert werden muss. Dies ist ein sehr häufiger Fall.

Der synchrone Zugriff wird verwendet, wenn erwartet wird, dass der Anruf wartet, bis sich alle Identitäten erledigt haben, bevor er fortgesetzt wird.

Asynchroner Zugriff ist "Feuer und Vergessen" und lässt die Identität in ihrer eigenen Zeit ihren neuen Zustand erreichen.

Arthur Ulfeldt
quelle
Wenn ich beim koordinierten Zugriff nur Änderungen vornehmen möchte state-a, aber dabei darauf verweise, state-bbrauche ich immer noch eine refkorrekte? Es ändert sich also nicht mehrere Dinge, sondern bezieht sich auf mehrere Dinge, während eines von ihnen geändert wird?
event_jr
2
Ja, Sie scheinen richtig zu verstehen, dass Zustand-a und Zustand-b beide refs sein müssen. Wenn Sie möchten, dass der neue Wert in Zustand-a auf einer konsistenten Kombination der Werte in a und b basiert. Sie müssen diesen neuen Wert in einem Kontext berechnet haben, in dem Zustand a und Zustand b miteinander übereinstimmen. Wenn beide Refs sind und sich b auf halbem Weg ändert, wird die Transaktion neu gestartet und die neuen Werte von a und b verwendet. Verwenden Sie die folgende ensureFunktion: clojure.github.io/clojure/clojure.core-api.html#clojure.core/… , um dies explizit und effizienter zu gestalten.
Arthur Ulfeldt
3
Vielleicht könnte eine Erklärung hinzugefügt werden, was Isoliert mit gemeinsam genutzten Standardmitteln bedeutet, um die Antwort zu vervollständigen?
Didier A.
1
"Der koordinierte Zugriff wird verwendet, wenn zwei Identitäten gemeinsam geändert werden müssen ...". Sollte das "geändert" werden?
Carcigenicate
40

Refs beziehen sich auf den Status, der zwischen Threads synchronisiert werden muss. Wenn Sie eine Reihe verschiedener Dinge im Auge behalten müssen und manchmal Vorgänge ausführen müssen, die auf mehrere Dinge gleichzeitig schreiben, verwenden Sie refs. Jedes Mal, wenn Sie mehrere verschiedene Statuselemente haben, ist die Verwendung von Refs keine schlechte Idee.

Atome sind für einen unabhängigen Zustand, der zwischen Threads synchronisiert werden muss. Wenn Sie niemals den Zustand des Atoms und irgendetwas anderes gleichzeitig ändern müssen, ist die Verwendung von at atom sicher (insbesondere wenn das gesamte Programm nur einen Zustand enthält, können Sie ihn in ein Atom einfügen). . Als nicht triviales Beispiel ist die Verwendung eines Atoms wahrscheinlich sicher, wenn Sie versuchen, die Rückgabewerte einer Funktion zwischenzuspeichern (dh zu merken) - der Status ist für alles außerhalb der Funktion unsichtbar, sodass Sie sich keine Sorgen machen müssen über eine Zustandsänderung innerhalb der Funktion, die irgendetwas durcheinander bringt.

Der Hauptpunkt der Agenten ist, dass sie in einem anderen Thread ausgeführt werden. Sie können den Wert des Agenten abrufen und ihn anweisen, eine Funktion auf seinen Wert anzuwenden, wissen jedoch nicht, wann die Funktion ausgeführt wird oder auf welchen Wert die Funktion angewendet wird.

Vars sind für den Fall gedacht, dass Sie etwas pro Thread speichern müssen. Wenn Sie ein Multithread-Programm haben und jeder Thread seinen eigenen privaten Status benötigt, setzen Sie diesen Status in eine Variable.

In Bezug auf Beispiele aus der Praxis können wir Ihnen sagen, was Sie verwenden sollen, wenn Sie ein Beispiel dafür geben, was Sie versuchen.

Retief
quelle
32

Als ich zum ersten Mal über diese Typen las, hatte ich auch Schwierigkeiten zu verstehen, wo ich sie verwenden könnte oder sollte. Hier ist meine einfache englische Antwort:

Verwenden Sie eine Variable, wenn sich die Daten nicht ändern. Dies geschieht immer dann, wenn Sie defoder die meisten Funktionen verwenden, die mit deflike beginnen defn.

Verwenden Sie ein Atom, wenn Sie ein einzelnes Element haben, das sich ändert. Ein Beispiel könnte ein Zähler oder ein Vektor sein, zu dem Sie Elemente hinzufügen möchten.

Verwenden Sie einen Ref, wenn Sie zwei oder mehr Dinge haben, die sich gleichzeitig ändern müssen. Denken Sie an "Datenbanktransaktionen", wenn Sie vertraut sind. Das kanonische Beispiel hierfür ist die Überweisung von Geld von einem Konto auf ein anderes. Jedes Konto kann in einer Referenz gespeichert werden, sodass Änderungen so vorgenommen werden können, dass sie atomar erscheinen.

Verwenden Sie einen Agenten, wenn Sie möchten, dass sich etwas ändert, aber es ist Ihnen egal, wann. Dies kann eine lange Berechnung sein oder etwas in eine Datei oder einen Socket schreiben. Beachten Sie, dass Sie mit letzterem verwenden sollten send-off.

Hinweis: Ich weiß zu schätzen, dass es in jedem von ihnen noch viel mehr gibt, aber hoffentlich sollte dies Ihnen einen Ausgangspunkt geben.

optevo
quelle
1
Vielen Dank für Ihre klare Antwort :-) Hilft einem Clojure-Neuling wie mir sehr.
Gosukiwi
27

Ich schrieb einen Artikel mit einer Zusammenfassung des Unterschieds zwischen ihnen und half bei der Auswahl, wann ich welche verwende.

Share state - bei Verwendung von Vars, Atomen, Agenten und Refs?

Ich hoffe, es wird Menschen helfen, Antworten zu diesem Thema zu suchen.

Einige Abkürzungen aus dem Artikel nach dem Vorschlag von @tunaci:

Vars

Vars sind für jeden Thread global.

Ändern Sie die Variablen nach dem Erstellen nicht. Es ist technisch möglich, aber aus vielen Gründen eine schlechte Idee.

Atome

Teilen Sie den Zugriff auf den veränderlichen Status für alle Threads. Änderung erfolgt synchron. Wiederholen Sie diesen Vorgang, wenn andere Threads während des Laufs den Status ändern.

Verwenden Sie keine nicht idempotenten Funktionen und Funktionen mit langer Ausführungsdauer

Agenten

Teilen Sie den Zugriff auf den veränderlichen Status für alle Threads. Die Änderung erfolgt asynchron.

Refs

Refs funktioniert ähnlich wie Datenbanktransaktionen. Schreiben und Lesen sind in Dosync geschützt. Sie können viele Refs sicher in der Transaktion bearbeiten.

Und Flussdiagramm, wenn Sie welches verwenden: Flussdiagramm

Bitte schauen Sie sich das Bild auf der Website an, da einige Updates immer möglich sind.

Es ist komplex und ein langes Thema, eine vollständige Antwort ohne Kopie und früheren Artikel zu geben. Bitte verzeihen Sie mir, ich leite Sie auf die Website weiter :)

Kabra
quelle