Pregunta

Soy consciente de que por lo general es una mala práctica de poner funciones con efectos secundarios dentro de las transacciones MCI, ya que potencialmente pueden ser juzgados y llamar varias veces.

Se me ocurre sin embargo, que usted podría utilizar agentes para garantizar que los efectos secundarios se ejecutan sólo después de que la transacción se completa con éxito.

por ejemplo.

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

¿Es esta buena práctica?

¿Cuáles son las ventajas / desventajas / trampas?

¿Fue útil?

Solución

Original:

Parece que deben trabajar para mí. Dependiendo de cuáles son sus efectos secundarios son, es posible que desee utilizar despedida (para operaciones de IO-ligados) en lugar de envío (para operaciones vinculadas a la CPU). El envío / despedida serán poner en cola la tarea en una de las piscinas agente ejecutor internos (hay una piscina de tamaño fijo para la CPU y piscina de tamaño ilimitado para ops IO). Una vez que se pone en cola la tarea, el trabajo está fuera del hilo dosync por lo que está desconectado en ese punto.

Usted necesita capturar todos los valores que necesita desde el interior de la transacción en la función enviado por supuesto. Y lo que necesita para hacer frente a que el envío se produce posiblemente varias veces debido a reintentos.

Actualizar (ver comentarios):

agente envía dentro de la transacción de que el árbitro se llevan a cabo hasta que la transacción ref completa con éxito y se ejecutan una vez. Así que en mi respuesta anterior, el envío no va a aparecer varias veces, sin embargo, no se producirá durante la transacción ref que puede no ser lo que quiere (si va a registrar o hacer cosas de lado effecty).

Otros consejos

Esto funciona y es una práctica común. Sin embargo, al igual que Alex ha señalado con razón que usted debe considerar despedida sobre de envío.

Hay más formas de capturar los valores comprometidos y la mano hacia fuera de la transacción. Por ejemplo, usted puede devolverlos en un vector (o un mapa o lo que sea).

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

o puede llamar al reinicio! en un átomo de local (definida fuera del alcance léxico del bloque dosync por supuesto).

No hay nada malo en usar agentes, sino simplemente que vuelve de los valores de las operaciones necesarias para efectuar el cómputo-lateral es a menudo suficiente.

Refs son probablemente la forma más limpia de hacer esto, pero incluso se puede manejar con sólo átomos!

(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"))))

El swap! puede volver a intentar tantas veces como sea necesario, pero la cola de trabajo nunca se hará más grande que tres, y siempre se imprimirá exactamente una vez un mensaje que está ligado correctamente a la aceptación de su trabajo articulo. El "diseño original" llamada por sólo un único int en el átomo, pero se puede convertir en un par con el fin de pasar de nuevo los datos interesantes de la computación.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top