Domanda

Sono consapevole che è generalmente cattiva pratica di mettere funzioni con effetti collaterali all'interno di transazioni MCS, in quanto possono potenzialmente essere processati nuovamente e chiamato più volte.

Mi viene in mente, tuttavia, che è possibile utilizzare gli agenti al fine di garantire che gli effetti collaterali vengono eseguiti solo dopo che la transazione viene completata correttamente.

es.

(dosync
  // transactional stuff
  (send some-agent #(function-with-side-effects params))
  // more transactional stuff
  )

E 'questa pratica buona?

Quali sono i pro / contro / insidie?

È stato utile?

Soluzione

originale:

Sembra che dovrebbe funzionare per me. A seconda di cosa gli effetti collaterali sono, si potrebbe desiderare di usare send-off (per ops IO-bound), invece di invio (per ops cpu-bound). Send / send-off saranno accodare il compito in una delle piscine agente esecutore interni (c'è una piscina dimensione fissa per CPU e piscina illimitata formato per OPS io). Una volta che il compito è accodato, il lavoro è fuori discussione del dosync così sei disconnesso a quel punto.

Avrete bisogno di catturare tutti i valori necessari dall'interno della transazione nella funzione inviato naturalmente. E hai bisogno di far fronte a tale invio, eventualmente, che si verificano più volte a causa di tentativi.

Aggiornamento (vedere commenti):

Agent invia all'interno della transazione del ref si tengono fino a quando la transazione ref viene completata con successo e vengono eseguite una volta. Quindi, nella mia risposta sopra, l'invio non si verificano più volte, tuttavia non si verificherà durante la transazione ref che non può essere ciò che si vuole (se si prevede di registrare o fare cose laterale effecty).

Altri suggerimenti

Questo funziona ed è una pratica comune. Tuttavia, come Alex ha giustamente sottolineato che si dovrebbe prendere in considerazione send-off nel corso di invio.

Ci sono più modi per catturare i valori commessi e la mano fuori della transazione. Per esempio si possono restituire in un vettore (o di una mappa o qualsiasi altra cosa).

(let [[x y z] (dosync
                ; do stuff
                [@x @y @z])] ; values of interest to sode effects
  (side-effect x y z))

oppure è possibile chiamare di reset! su un atomo locale (definito fuori dell'ambito lessicale del blocco dosync naturalmente).

Non c'è niente di sbagliato con l'utilizzo di agenti, ma semplicemente ritornando ai valori di transazione necessari per il calcolo laterale effettuando è spesso sufficiente.

Refs sono probabilmente il modo più pulito per fare questo, ma si può anche gestire con solo atomi!

(def work-queue-size (atom [0]))

(defn add-job [thunk]
  (let [[running accepted?]
        (swap! work-queue-size
               (fn [[active]]
                 (if (< active 3)
                   [(inc active) true]
                   [active false])))]
    (println
     (str "Your job has been "
          (if accepted?
            "queued, and there are "
            "rejected - there are already ")
          running
          " total running jobs"))))

Il swap! può ripetere tante volte quanto necessario, ma la coda di lavoro non sarà mai più grande di tre, e sarà sempre stampare esattamente una volta un messaggio che è legato in modo corretto l'accettazione del vostro lavoro articolo. Il "design originale" chiamato solo per un singolo int nell'atomo, ma è possibile trasformarlo in un paio per passare dati interessanti di nuovo fuori del calcolo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top