Clojure: коммутация перед изменением в рамках одной транзакции приводит к сбою транзакции

Я новичок в Clojure и пытаюсь понять его модель транзакций. Играя с alter и commute, я заметил, что если я alter ref после commute, то транзакция ничего не зафиксирует (или ничего не изменит).

Например:

(def counter (ref 0))
(def i (ref 0))
(future (dosync
          (ref-set counter 1)
          (ref-set i 1)
          (commute counter inc)
          (alter counter inc)))

И @counter, и @i будут равны 0, но если я поменяю местами commute и alter или использую два commute или два alter в этом случае, это даст желаемый результат (3 и 1 соответственно).

Я читал некоторые сообщения, объясняющие, что поведение commute и alter немного отличается тем, что commute фактически выполняется дважды в транзакции (один там, где он стоит, другой на этапе «фиксации») и игнорирует несогласованные снимки ref . Меня просто смущает странное поведение комбинации этих двух.

Может ли кто-нибудь помочь объяснить, как это работает? Заранее спасибо!


person kkaatii    schedule 15.02.2017    source источник
comment
clojure 1.8: он выводит в repl {:status :failed, :val #error { :cause "Can't set after commute" :via [{:type java.util.concurrent.ExecutionException ..., поэтому не делайте alter после commute. Не удалось найти какой-либо конкретный документ для него — только источник   -  person birdspider    schedule 15.02.2017


Ответы (1)


Функция commute полезна только в очень узких (т. е. редких) случаях, когда она может снизить количество конфликтов при блокировке за счет дополнительных повторных попыток функции обновления. Это также значительно усложняет ментальную модель транзакции, как показывает ваш пример (например, я никогда раньше не сталкивался с этой конкретной проблемой).

ИМХО, почти всегда лучше использовать alter вместо commute, так как alter проще и надежнее. На самом деле, я бы обычно рассматривал использование commute как случай преждевременной оптимизации.

person Alan Thompson    schedule 15.02.2017