Подтвердить что ты не робот

Как ведут себя вложенные вызовы dosync?

Что происходит при создании вложенных вызовов dosync? Будут ли выполняться суб-транзакции в родительской области? Являются ли эти под-транзакции обратимыми, если родительская транзакция терпит неудачу?

4b9b3361

Ответ 1

Если вы имеете в виду синтаксическое вложение, тогда ответ зависит от того, будет ли внутренний dosync работать в том же потоке, что и внешний.

В Clojure, всякий раз, когда вводится блок dosync, новая транзакция запускается, если она еще не была запущена в этом потоке. Это означает, что, хотя выполнение остается в одном потоке, внутренние транзакции можно считать включенными внешними транзакциями; однако, если a dosync занимает позицию, синтаксически вложенную в другой dosync, но, случается, запускается в новом потоке, у нее будет новая транзакция для себя.

Пример, который (надеюсь) иллюстрирует, что происходит:

user> (def r (ref 0))
#'user/r
user> (dosync (future (dosync (Thread/sleep 50) (println :foo) (alter r inc)))
              (println :bar)
              (alter r inc))
:bar
:foo
:foo
1
user> @r
2

"Внутренняя" транзакция повторяет после печати :foo; "внешняя" транзакция никогда не должна перезапускаться. (Обратите внимание, что после этого происходит генерация цепочек истории r, поэтому, если "большая" форма dosync была оценена во второй раз, внутренний dosync не будет повторять попытку. Он все равно не будет объединен с внешний, конечно.)

Кстати, Марк Волкманн написал фантастическую статью о Clojure Software Transactional Memory; это настоятельно рекомендуется прочитать для всех, кто заинтересован в том, чтобы получить глубокое понимание деталей такого рода.