Załóżmy, że masz kilka kont bankowych:
(def accounts
[(ref 0)
(ref 10)
(ref 20)
(ref 30)])
I atomowa funkcja „transferu”:
(defn transfer [src-account dest-account amount]
(dosync
(alter dest-account + amount)
(alter src-account - amount)))
Który działa w następujący sposób:
(transfer (accounts 1) (accounts 0) 5)
(map deref accounts)
=> (5 5 20 30)
Następnie możesz łatwo skomponować funkcję transferu, aby utworzyć transakcję wyższego poziomu, na przykład przesyłanie z wielu kont:
(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)
Pamiętaj, że wszystkie wielokrotne przelewy miały miejsce w jednej, połączonej transakcji, tzn. Możliwe było „skomponowanie” mniejszych transakcji.
Aby to zrobić z blokadami, bardzo szybko się skomplikuje: zakładając, że konta muszą być indywidualnie blokowane, musisz zrobić coś takiego jak ustanowienie protokołu dotyczącego kolejności uzyskiwania blokady, aby uniknąć zakleszczeń. Bardzo łatwo jest popełnić trudny do wykrycia błąd. STM ratuje cię przed całym tym bólem.