Вот пример 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)
Обратите внимание, что все множественные переводы происходили в одной комбинированной транзакции, т. е. было возможно "составить" меньшие транзакции.
Чтобы сделать это с блокировками, это очень быстро усложнилось бы: если учесть, что учетные записи должны быть индивидуально заблокированы, вам нужно будет сделать что-то вроде установления протокола порядка получения блокировок, чтобы избежать взаимных блокировок.Как справедливо отмечает Джон, в некоторых случаях вы можете сделать это, отсортировав все блокировки в системе, но в большинстве сложных систем это невозможно.Сделать ошибку, которую трудно обнаружить, очень легко.СТМ спасает вас от всей этой боли.