sleeping…#
simple multithreading , from the Brave clojure book#
(future (Thread/sleep 4000)
(println "I'll print after 4 seconds"))
(println "I'll print immediately")
hmm this is weird. so dereferencing the future blocks?#
(defn fight-crime
[]
(let []
(println "hi")
(Thread/sleep 2000)
(println "ho")
(Thread/sleep 1000)
(println "yo")
5
))
(let [result (future (fight-crime))]
(println "@: " @result)
(println "snore. " )
(println "@: " @result)
(Thread/sleep 1000)
(println "@: " @result)
)
- Ah ok, but you can stop waiting.. so here we wait
10ms
and then return “hmmf” if future isnt done yet.
(let [result (future (fight-crime))]
(println "@: " (deref result 10 "hmmf"))
(println "snore. " )
(println "@: " (deref result 10 "hmmf"))
(Thread/sleep 5000)
(println "@: " (deref result 10 "hmmf"))
)
delays. cool#
(def jackson-5-delay
(delay (let [message "Just call my name and I'll be there"]
(println "First deref:" message)
message)))
(def gimli-headshots ["serious.jpg" "fun.jpg" "playful.jpg"])
(defn email-user
[email-address]
(println "Sending headshot notification to" email-address))
(defn upload-document
"Needs to be implemented"
[headshot]
true)
(let [notify (delay (email-user "and-my-axe@gmail.com"))]
(doseq [headshot gimli-headshots]
(future (upload-document headshot)
(force notify))))
Blocking thread in action…#
; make a future which blocks on a promise. Then deliver promise in main thread and see what happens.
(def a-promise (promise))
(def futr
(future
(def result (+ @a-promise 10))
(Thread/sleep 5000)
(println "result " result)
result))
;
(println "nothing yet: " (deref futr 10 "nothing."))
; deliver..
(deliver a-promise 99)
(println "right after deliverin " (deref futr 10 "still nothin."))
(Thread/sleep 5500)
(println "had some time to think... " (deref futr))
delivering to promise multiple times?#
- hmm, how about the other way around… make some workers do something and have them all try deliver same promise. What will happen?
(def another-promise (promise))
(def d1 (delay
(Thread/sleep 6000)
(deliver another-promise 6000)
))
;
(def d2 (delay
(Thread/sleep 4000)
(deliver another-promise 4000)
))
;
(def d3 (delay
(Thread/sleep 1000)
(deliver another-promise 1000)
))
; nothing there right?
(realized? another-promise)
; now run them all...
(doseq [a-worker [d1 d2 d3]]
(future (force a-worker))
)
(println "Check promise: " (deref another-promise 10 "nothin."))
(println "Check promise: " @another-promise)
- hmm, for the above, strange that I tried but was not able to
deref
the delays. Error was
ClassCastException clojure.lang.Delay cannot be cast to java.util.concurrent.Future clojure.core/deref-future (core.clj:2206)
- given the yak butter search data…
(def yak-butter-international
{:store "Yak Butter International"
:price 90
:smoothness 90})
(def butter-than-nothing
{:store "Butter Than Nothing"
:price 150
:smoothness 83})
;; This is the butter that meets our requirements
(def baby-got-yak
{:store "Baby Got Yak"
:price 94
:smoothness 99})
(defn mock-api-call
[result]
(Thread/sleep 1000)
result)
(defn satisfactory?
"If the butter meets our criteria, return the butter, else return false"
[butter]
(and (<= (:price butter) 100)
(>= (:smoothness butter) 97)
butter))
- The original non concurrent code was …
(time (some (comp satisfactory? mock-api-call)
[yak-butter-international butter-than-nothing baby-got-yak]))
; => "Elapsed time: 3002.132 msecs"
; => {:store "Baby Got Yak", :smoothness 99, :price 94}
- and a concurrent version…
(defn blah-func
[]
(def best-butter-promise (promise))
(time
(doseq [some-butter [yak-butter-international butter-than-nothing baby-got-yak]]
(future (if ((comp satisfactory? mock-api-call) some-butter)
(deliver best-butter-promise some-butter)
)))
)
(time
(println "result is: " @best-butter-promise))
)
(blah-func)
=>
cloj-multiproc-play.core=> (blah-func)
"Elapsed time: 0.615436 msecs"
result is: {:store Baby Got Yak, :price 94, :smoothness 99}
"Elapsed time: 1001.655823 msecs"
nil
cloj-multiproc-play.core=>
one more example , racing Bing vs Google#
(def search-result-promise (promise))
(def bingurl "https://www.bing.com/search?q=foobar")
(def googleurl "https://www.google.com/?gws_rd=ssl#q=foobar")
(doseq [url [bingurl googleurl]]
(future (deliver search-result-promise (slurp url)
))
)
(def html1 (slurp "https://www.google.com/?gws_rd=ssl#q=foobar"))
- my version of a hot dog machine that only dispenses max number of hot dogs…
(defn hot-dog-machine-v2
[how-many-dogs-init]
(let [in (chan)
out (chan)]
(go
(loop [num-dogs how-many-dogs-init]
; if no hot dogs left, then done.
(if (= num-dogs 0)
true
; Otherwise
(do
(println "Going to <! block on <in> now.")
(<! in)
(println "Now we have " num-dogs " dogs.")
(println "Going to >! block on <out> now.")
(>! out "hot dog")
))
(recur (dec num-dogs))
))
[in out]
))
; get channels..
(def machine-channels (hot-dog-machine-v2 5))
(def in (first machine-channels))
(def out (last machine-channels))
; ... dispensing them
(println "one")
(>!! in "pocket lint")
(<!! out)
(println "two")
(>!! in "pocket lint")
(<!! out)
(println "three")
(>!! in "pocket lint")
(<!! out)
(println "four")
(>!! in "pocket lint")
(<!! out)
(println "five")
(>!! in "pocket lint")
(<!! out)
....
; that was fine except we went into negative hot dogs...
playsync.core=> (println "five")
five
nil
playsync.core=> (>!! in "pocket lint")
trueNow we have 1 dogs.
Going to >! block on <out> now.
playsync.core=> (<!! out)
Going to <! block on <in> now.
"hot dog"
playsync.core=>
playsync.core=> (>!! in "pocket lint")
Now we have -1 dogs.
true
Going to >! block on <out> now.
playsync.core=> (<!! out)
"hot dog"Going to <! block on <in> now.
(let [results-chan (chan)
results-vector (atom [])]
; For several things in stuff-vec , start some async call that will throw results,
; into the results-chan
(doseq [x stuff-vec]
(do-something-async results-chan x))
; in this case, we take from the results-chan once they are ready,
; and update (using conj) the atom results-vector
(doseq [_ stuff-vec]
(swap! results-vector conj (<!! results-chan)))
)
swap syntax for updating atoms…#