Skip to content

Instantly share code, notes, and snippets.

@bvandgrift
Created January 7, 2016 18:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvandgrift/8e0e58463efda84f44ac to your computer and use it in GitHub Desktop.
Save bvandgrift/8e0e58463efda84f44ac to your computer and use it in GitHub Desktop.
placing your peek inside of a dosync avoids potential thread conflict (from Clojure Applied forum)
;; response to: https://forums.pragprog.com/forums/352/topics/13835
;; this is a quick demonstration of the pitfalls in querying for a ref
;; outside of the accompanying transaction in which you plan on changing it.
(import '(java.util.concurrent Executors))
;; start list
(def slist (ref #{}))
;; end list
(def elist (ref []))
;; initialize the lists
(defn init-lists[]
(dosync
(ref-set slist (into #{} (range 50)))
(ref-set elist [])))
(init-lists)
;; let outside of a dosync block invites
;; problems
(defn move-item []
(let [i (first @slist)]
(dosync
(Thread/sleep (rand 100))
(if i
(do
(alter elist conj i)
(alter slist disj i))))))
;; invoke a number of threads to execute moves
(defn test-moves [nthreads f]
(init-lists)
(let [pool (Executors/newFixedThreadPool nthreads)]
(dotimes [n 100] ;; account for duplicates
(.submit pool f))
(.shutdown pool)
(while (not (.isTerminated pool)) (Thread/sleep 500))))
;; moving the let inside the dosync block
;; fixes this problem, as the transaction maintains
;; the integrity of slist throughout
(defn move-item2 []
(dosync
(let [i (first @slist)]
(Thread/sleep (rand 100))
(if i (do
(alter elist conj i)
(alter slist disj i))))))
;; use 10 threads, takes ~3 secs on my box
(time (test-moves 10 move-item))
;; witness the duplication
(sort @elist)
;; takes ~8 seconds
(time (test-moves 10 move-item2))
;; and now ...
(sort @elist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment