Вот пример Clojure:
Предположим, у вас есть вектор банковских счетов (в реальной жизни вектор может быть несколько длиннее ....):
(def accounts
[(ref 0)
(ref 10)
(ref 20)
(ref 30)])
(map deref accounts)
=> (0 10 20 30)
И функция «перевода», которая безопасно переводит сумму между двумя счетами за одну транзакцию:
(defn transfer [src-account dest-account amount]
(dosync
(alter dest-account + amount)
(alter src-account - amount)))
Что работает следующим образом:
(transfer (accounts 1) (accounts 0) 5)
(map deref accounts)
=> (5 5 20 30)
Затем вы можете легко составить функцию передачи для создания транзакции более высокого уровня, например, для перевода с нескольких учетных записей:
(defn transfer-from-all [src-accounts dest-account amount]
(dosync
(doseq [src src-accounts]
(transfer src dest-account amount))))
(transfer-from-all
[(accounts 0) (accounts 1) (accounts 2)]
(accounts 3)
5)
(map deref accounts)
=> (0 0 15 45)
Обратите внимание, что все множественные переводы произошли в одной комбинированной транзакции, т.е. можно было «составить» более мелкие транзакции.
Сделать это с помощью блокировок будет очень сложно: если предположить, что учетные записи должны быть индивидуально заблокированы, вам нужно будет сделать что-то вроде установления протокола для порядка получения блокировки, чтобы избежать взаимоблокировок. Как справедливо отмечает Джон, в некоторых случаях это можно сделать, отсортировав все блокировки в системе, но в большинстве сложных систем это невозможно. Совершить ошибку, которую трудно обнаружить, очень легко. STM избавляет вас от всей этой боли.
person
mikera
schedule
06.04.2011