2011-01-22 11 views
7

Soy consciente de que generalmente es una mala práctica poner funciones con efectos secundarios en las transacciones de STM, ya que se pueden volver a intentar y llamar varias veces.Uso de agentes para completar efectos secundarios en transacciones de STM

Sin embargo, se me ocurre que podría usar agentes para garantizar que los efectos secundarios se ejecuten solo después de que la transacción se complete satisfactoriamente.

p. Ej.

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

¿Es esta una buena práctica?

¿Cuáles son los pros/contras/trampas?

+0

Una de las ideas centrales de STM es la atomicidad de fallas. ¿Cómo ayudaría esto con eso? –

+0

El punto es por los efectos secundarios que deben ocurrir después de que una transacción tiene éxito pero no son parte de la transacción, p. enviando un correo electrónico de confirmación. Claramente, no desea hacer esto cada vez que la transacción reintenta o de lo contrario podría obtener un destinatario muy enojado/confundido. – mikera

Respuesta

6

original:

parece que eso debería funcionar para mí. Dependiendo de cuáles sean sus efectos secundarios, es posible que desee utilizar envío (para operaciones vinculadas a IO) en lugar de enviar (para operaciones vinculadas a CPU). El envío/envío colocará la tarea en uno de los grupos de ejecutores internos del agente (hay un grupo de tamaño fijo para la CPU y un grupo de tamaño ilimitado para las operaciones io). Una vez que la tarea está en cola, el trabajo está fuera del hilo de dosync, por lo que estás desconectado en ese punto.

Deberá capturar los valores que necesite desde dentro de la transacción hasta la función enviada, por supuesto. Y necesita lidiar con ese envío posiblemente ocurriendo varias veces debido a reintentos.

Update (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 ejecuta una vez. Por lo tanto, en mi respuesta anterior, el envío NO ocurrirá varias veces, sin embargo, no ocurrirá durante la transacción de refuencia, que puede no ser lo que usted desea (si espera iniciar sesión o hacer algo con efecto secundario).

+0

@mikera, @Alex, se garantiza que el envío se producirá una sola vez después de que la transacción tenga éxito, y no debe ocurrir varias veces. (Declaración 5 en http://clojure.org/agents) – bmillare

+0

@bmillare - Creo que estás hablando de otra cosa. Esa declaración se refiere a cómo se ejecutará la función aprobada en un envío/envío. Lo que estoy diciendo es que al hacer esa llamada dentro de la transacción ref, la transacción * ref puede ser reintentada causando múltiples llamadas de envío/expulsión, cada una de las cuales se ejecuta exactamente una vez. –

+0

@alex, discúlpeme, hice referencia al punto equivocado, más adelante en el texto, dice "Los agentes están integrados con el STM: todos los envíos realizados en una transacción se retienen hasta que se comprometan, y se descartan si se vuelven a intentar o cancelan. " – bmillare

5

Esto funciona y es una práctica común. Sin embargo, como bien señaló Alex, debería considerar enviar más que enviar.

Existen más formas de capturar valores comprometidos y distribuirlos de la transacción. Por ejemplo, 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 a restablecer! en un átomo local (definido fuera del alcance léxico del bloque dosync, por supuesto).

1

No hay nada de malo con el uso de agentes, pero basta con volver de los valores de transacción necesarios para el cálculo de efectos secundarios a menudo.

Las referencias son probablemente la forma más limpia de hacerlo, ¡pero incluso se puede gestionar con solo á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 va a ser mayor que tres, y siempre se imprimirá exactamente una vez un mensaje que está ligado correctamente a la aceptación de su elemento de trabajo . El "diseño original" requería solo un int en el átomo, pero puede convertirlo en un par para pasar datos interesantes fuera del cómputo.

Cuestiones relacionadas