Использование агентов для завершения побочных эффектов в транзакциях STM - PullRequest
8 голосов
/ 22 января 2011

Мне известно, что, как правило, плохая практика - помещать функции с побочными эффектами в транзакции STM, поскольку они потенциально могут быть повторены и вызваны несколько раз.агенты, обеспечивающие выполнение побочных эффектов только после успешного завершения транзакции.

например,

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

Это хорошая практика?

Какие плюсы / минусы / подводные камни?

Ответы [ 3 ]

7 голосов
/ 22 января 2011

Оригинал:

Кажется, это должно работать для меня. В зависимости от ваших побочных эффектов, вы можете использовать send-off (для операций, связанных с IO), а не send (для операций, связанных с процессором). Отправка / отправка поставит задачу в один из пулов исполнителей внутреннего агента (есть пул фиксированного размера для процессора и пул неограниченного размера для операций ввода-вывода). Как только задача поставлена ​​в очередь, работа выходит из потока dosync, поэтому в этот момент вы отключаетесь.

Вам, конечно, нужно будет захватить любые значения из транзакции в отправленной функции. И вам нужно иметь дело с этой отправкой, которая может происходить несколько раз из-за повторных попыток.

Обновление (см. Комментарии):

Посылки агента в транзакции ref удерживаются до тех пор, пока транзакция ref успешно не завершится и не будут выполнены один раз. Таким образом, в моем ответе выше отправка НЕ ​​будет происходить несколько раз, однако она не произойдет во время транзакции ref, которая может не соответствовать вашим ожиданиям (если вы планируете войти или выполнить побочные действия).

5 голосов
/ 23 января 2011

Это работает и является обычной практикой.Однако, как правильно заметил Алекс, вы должны рассмотреть отправку через отправку.

Существуют и другие способы получения зафиксированных значений и передачи их из транзакции.Например, вы можете вернуть их в векторе (или на карте или где-либо еще).

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

или вы можете вызвать сброс!на локальном атоме (определенном вне лексического контекста блока dosync, конечно).

1 голос
/ 12 мая 2011

Нет ничего плохого в использовании агентов, но простого возврата из значений транзакций, необходимых для побочного вычисления, часто достаточно.

Ссылки, вероятно, являются самым чистым способом сделать это, но вы даже можете управлять этимтолько с атомами!

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

swap! может повторить столько раз, сколько необходимо, но рабочая очередь никогда не будет больше трех, и вы всегда будете печатать ровно один раз aсообщение, которое правильно связано с принятием вашего рабочего элемента.«Оригинальный дизайн» требовал только одного целого в атоме, но вы можете превратить его в пару, чтобы передать интересные данные из вычислений.

...