Clojure differenze tra Ref, Var, Agent, Atom, con esempi


110

Sono molto nuovo a Clojure, ragazzi, potete darmi una spiegazione con scenari del mondo reale. Voglio dire, dove usare Ref, Var, Agent, Atom. Ho letto il libro, ma ancora non riuscivo a capire gli esempi del mondo reale.

Risposte:


174

Consiglio vivamente "The Joy of Clojure" o "programmazione Clojure" per una risposta reale a questa domanda, posso riprodurre un breve frammento delle motivazioni per ciascuno:

inizia guardando questo video sulla nozione di identità e / o studiando qui .

  • I riferimenti sono per l' accesso sincrono coordinato a "Molte identità".
  • Gli atomi sono per l' accesso sincrono non coordinato a una singola identità.
  • Gli agenti sono per l' accesso asincrono non coordinato a una singola identità.
  • Le variabili sono per identità isolate locali di thread con un valore predefinito condiviso.

L' accesso coordinato viene utilizzato quando due identità devono cambiare insieme, l'esempio classico è spostare denaro da un conto bancario a un altro, deve spostarsi completamente o per niente.

L' accesso non coordinato viene utilizzato quando è necessario aggiornare solo un'identità, questo è un caso molto comune.

L' accesso sincrono viene utilizzato quando si prevede che la chiamata attenda fino a quando tutte le identità non sono state risolte prima di continuare.

L' accesso asincrono è "spara e dimentica" e lascia che l'identità raggiunga il suo nuovo stato a suo tempo.


In accesso coordinato, se voglio solo cambiare state-a, ma state-bnel farlo mi riferisco , ho comunque bisogno di un ref? Quindi non sta cambiando più cose ma si riferisce a più cose mentre ne cambia una?
event_jr

2
Sì, sembra che tu capisca correttamente che lo stato-a e lo stato-b devono essere entrambi ref Se vuoi che il nuovo valore nello stato-a sia basato su una combinazione coerente dei valori in a e b. È necessario che il nuovo valore sia stato calcolato in un contesto in cui state-ae e state-b erano coerenti tra loro. Quando sono entrambi riferimenti, se b cambia a metà, la transazione verrà riavviata e utilizzerà i nuovi valori sia di a che di b. considera l'utilizzo della ensurefunzione: clojure.github.io/clojure/clojure.core-api.html#clojure.core/… per renderlo esplicito e più efficiente.
Arthur Ulfeldt

3
Forse una spiegazione di cosa significa Isolato con predefinito condiviso potrebbe essere aggiunta per completare la risposta?
Didier A.

1
"L'accesso coordinato viene utilizzato quando due identità devono essere modificate insieme ...". Dovrebbe essere "cambiato"?
Carcigenicate

40

I riferimenti sono per lo stato che deve essere sincronizzato tra i thread. Se hai bisogno di tenere traccia di un sacco di cose diverse ea volte avrai bisogno di fare operazioni che scrivono su molte cose contemporaneamente, usa refs. Ogni volta che hai più pezzi di stato diversi, usare gli arbitri non è una cattiva idea.

Gli atomi sono per uno stato indipendente che deve essere sincronizzato tra i thread. Se non avrai mai bisogno di cambiare lo stato dell'atomo e qualsiasi altra cosa allo stesso tempo, usare at atom è sicuro (in particolare, se c'è un solo pezzo di stato nell'intero programma, puoi metterlo in un atomo) . Come esempio non banale, se stai cercando di memorizzare nella cache i valori di ritorno di una funzione (cioè memorizzarla), l'uso di un atomo è probabilmente sicuro: lo stato è invisibile a tutto ciò che è al di fuori della funzione, quindi non devi preoccuparti su un cambiamento di stato all'interno della funzione che incasina qualcosa.

Il punto principale degli agenti è che vengono eseguiti in un thread diverso. È possibile ottenere il valore dell'agente e dirgli di applicare una funzione al suo valore, ma non si sa quando verrà eseguita la funzione o a quale valore verrà applicata.

Le Vars servono quando è necessario memorizzare qualcosa in base ai thread. Se hai un programma multi-thread e ogni thread necessita del proprio stato privato, metti quello stato in un var.

Per quanto riguarda gli esempi del mondo reale, se fornisci un esempio di ciò che stai cercando di fare, possiamo dirti cosa usare.


32

Quando ho letto per la prima volta di questi tipi, ho anche faticato a capire dove potevo o dovevo usarli, quindi ecco la mia semplice risposta in inglese:

Usa una variabile quando i dati non cambieranno. Questo accade ogni volta che usi defo la maggior parte delle funzioni che iniziano con deflike defn.

Usa un atomo quando hai un singolo elemento che cambia. Un esempio potrebbe essere un contatore o un vettore a cui si desidera aggiungere elementi.

Usa un riferimento quando hai due o più cose che devono cambiare allo stesso tempo. Pensa alle "transazioni di database" se conosci. L'esempio canonico di questo è il trasferimento di denaro da un conto a un altro. Ogni account può essere memorizzato in un riferimento in modo che le modifiche possano essere apportate in modo che appaiano atomiche.

Usa un agente quando vuoi che qualcosa cambi ma non ti interessa quando. Potrebbe trattarsi di un lungo calcolo o di scrivere qualcosa su un file o un socket. Nota che con quest'ultimo dovresti usare send-off.

Nota: apprezzo che ci sia molto di più in ognuno di questi, ma spero che questo dovrebbe darti un punto di partenza.


1
Grazie mille per la tua chiara risposta :-) Aiuta molto un principiante di Clojure come me.
gosukiwi

27

Ho scritto un articolo con un riepilogo della differenza tra loro e aiutare a scegliere quando utilizzare quale.

Condividi stato: quando si usano variabili, atomi, agenti e riferimenti?

Spero che aiuti le persone a cercare risposte in questo argomento.

Qualche scorciatoia dall'articolo dopo il suggerimento di @tunaci:

Vars

Le Vars sono globali per ogni thread.

Non modificare le variabili dopo la creazione. È tecnicamente possibile, ma è una cattiva idea per molte ragioni.

atomi

Condividi l'accesso allo stato mutevole per ogni thread. Il cambiamento avviene in modo sincrono. Riprova quando un altro thread cambia lo stato durante l'esecuzione.

Non utilizzare funzioni e funzioni non idempotenti con esecuzione a lungo termine

Agents

Condividi l'accesso allo stato mutevole per ogni thread. La modifica avviene in modo asincrono.

refs

Refs funziona in modo simile alle transazioni del database. La scrittura e la lettura sono protette in dosync. Puoi operare su molti ref in sicurezza nella transazione.

E diagramma di flusso quando usi quale: diagramma di flusso

Si prega di guardare l'immagine sul sito web, perché alcuni aggiornamenti sono sempre possibili.

È un argomento complesso e lungo per dare una risposta completa senza copia e articolo precedente, quindi per favore perdonami, ti reindirizzo al sito web :)


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.