Skip to content

Instantly share code, notes, and snippets.

@fahrrad
Last active October 24, 2017 15:40
Show Gist options
  • Save fahrrad/e25f46e4407e53f5f93f6f7e93c8f2c6 to your computer and use it in GitHub Desktop.
Save fahrrad/e25f46e4407e53f5f93f6f7e93c8f2c6 to your computer and use it in GitHub Desktop.
(ns deal-picker.atoms-and-vars
(:require [clojure.set :as set]))
;; Vars are the normal way that you define symbolic names for values
(def ^:dynamic x 10)
(def some-fn (fn [x] (+ x 2)))
;; Beware, they are namespace global scoped, don't do this!
(defn my-double-sum [a b]
(let [x (+ a b)]
(def x (* 2 x))))
;; This is how we can use vars in tests
(def ^:dynamic db
"DB2")
;; Redefs redefine the value a var is pointing to
(defn test-db-redefs []
(println "DB is configured to be " db)
(with-redefs [db "Test DB"]
(dotimes [_ 3]
(Thread/sleep (+ 100 (rand-int 300)))
(println
(.getName (Thread/currentThread))
" is connecting to " db))))
;;; To be Thread safe, vars can be rebound per thread
(defn access-db [connect-to]
(binding [db connect-to]
(dotimes [_ 3]
(Thread/sleep (rand-int 300))
(println
(.getName (Thread/currentThread))
" is connecting to " db))))
(defn test-access-db-thread []
(.start (Thread. #(access-db "Postgres")))
(.start (Thread. #(access-db "Oracle")))
(.start (Thread. #(access-db "RDBMS"))))
;; Atoms are a way to share a global state over different threads.
;; They are suited for noncoordinated, synchronous action on shared state
;; Good use case for them is a counter:
(def counter (atom 0))
(defn get-next-counter []
(swap! counter inc))
;; This is NOT a good use case: The action contains? has to be
;; eecuted before swap!. This is a operation that needs coordination
(def atom-cache (atom #{}))
(defn is-id-used-before-atom? [id]
(if (contains? @atom-cache id)
:error
(do (Thread/sleep (rand-int 1000))
(swap! atom-cache conj id))))
(defn start-thread-atom []
(.start
(Thread.
#(dotimes [i 10]
(println
(.getName (Thread/currentThread))
" checking id " i " : " (is-id-used-before-atom? i))))))
(defn run-multiple-access-threads-atoms []
(reset! atom-cache #{})
(dotimes [_ 3] (start-thread-atom)))
;; That's where STM (software transactional memory) comes in.
;; Refs
;; This will gives us a construct: dosync that makes sure that
;; the value was not changed in between. If it did, it will rerun the transaction
(def cache (ref #{}))
(defn is-id-used-before? [id]
(dosync
(if (contains? @cache id)
:error
(do (Thread/sleep (rand-int 1000))
(alter cache conj id)))))
(defn start-thread-ref []
(.start (Thread.
#(dotimes [i 10]
(println
(.getName (Thread/currentThread))
" checking id " i " : " (is-id-used-before? i))))))
(defn run-multiple-access-threads-refs []
(dosync (ref-set cache #{}))
(dotimes [_ 3] (start-thread-ref)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment