Skip to content

Instantly share code, notes, and snippets.

@BadUncleX
Last active March 31, 2018 11:33
Show Gist options
  • Save BadUncleX/7fbac99fcabb8a64fc71fb0020e24e4a to your computer and use it in GitHub Desktop.
Save BadUncleX/7fbac99fcabb8a64fc71fb0020e24e4a to your computer and use it in GitHub Desktop.
STM usage ref and dosync (alter , commute, ensure)
(def total-donations (ref 0))
(def count-donations (ref 0))
;; start 9 people collecting money
(comment
(dotimes [_ 9]
(doto (Thread. (fn []
;; go collect $10
;; ...
(dosync
;; record $10
(alter total-donations + 10)
;; record one donation
(alter count-donations inc))
;; do it again
(recur)))
.start)))
;; start one person tweeting the total
(comment
(doto (Thread. (fn []
;; wait 100 seconds
(Thread/sleep 10000)
(println (str "We collected $" @total-donations " total!"))))
.start))
;; start one person tweeting the average
(comment
(doto (Thread. (fn []
;; wait 100 seconds
(Thread/sleep 10000)
(when (pos? @count-donations)
(println (str "Average donation: $" (double (dosync (/ @total-donations @count-donations))))))))
.start))
;; dosync with alter
(def makoto-account (ref {:name "Makoto Hashimoto" :amount 1000}))
(def nico-account (ref {:name "Nicolas Modrzyk" :amount 2000}))
(defn transfer! [from to amount]
(dosync (println "transfer money from " (:name @from) " to " (:name @to) " amount = " amount " begins")
(alter from assoc :amount (- (:amount @from) amount))
(Thread/sleep 500) (alter to assoc :amount (+ (:amount @to) amount))
(println "Now, " (:name @from) " amount is " (:amount @from) " and " (:name @to) " amount is " (:amount @to)) ))
(comment
(transfer! makoto-account nico-account 10))
(comment
(do
(future (transfer! makoto-account nico-account 200))
(future (transfer! makoto-account nico-account 300))))
;; 使用ensure按序执行,不用竞争
;; 这不就是加锁么?
;; No, 还保证只允许在dosync的transaction之下运行, 直接reset不允许.
(defn ensure-transfer! [from to amount]
(dosync (ensure from)
(println "transfer money from " (:name @from) " to " (:name @to) " amount = " amount " begins")
(alter from assoc :amount (- (:amount @from) amount))
(Thread/sleep 500)
(alter to assoc :amount (+ (:amount @to) amount))
(println "Now, " (:name @from) " amount is " (:amount @from) " and " (:name @to) " amount is " (:amount @to))))
(comment
(do
(future (ensure-transfer! nico-account makoto-account 100))
(future (ensure-transfer! nico-account makoto-account 200))))
;; add watcher for ref
;;
(defn add-watcher [ref]
(add-watch ref :watcher
(fn [_ _ old-state new-state]
(prn "---- ref changed --- " old-state " => " new-state) )))
(add-watcher makoto-account)
(add-watcher nico-account)
(defn refined-transfer! [from to amount]
(dosync (ensure from)
(if (<= (- (:amount @from) amount) 0)
(throw (Exception. "insufficient amount")))
(alter from assoc :amount (- (:amount @from) amount))
(Thread/sleep 500)
(alter to assoc :amount (+ (:amount @to) amount))))
;;Let's set values of refs using ref-set:
(comment
(do (ref-set makoto-account {:name "Makoto Hashimoto" :amount 1000})
(ref-set nico-account {:name "Nicolas Modrzyk" :amount 2000})) )
;;=> IllegalStateException No transaction running
(comment
(dosync (ref-set makoto-account {:name "Makoto Hashimoto" :amount 1000})
(ref-set nico-account {:name "Nicolas Modrzyk" :amount 2000})
))
;; let's test concurrently
(comment
(do
(future (refined-transfer! makoto-account nico-account 100))
(future (refined-transfer! nico-account makoto-account 300))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment