Created
August 13, 2019 19:19
-
-
Save matthewdowney/5b3da8798df476d5a382e93fedb1d507 to your computer and use it in GitHub Desktop.
A `do` macro in clojure for concurrent execution of worker code with ordered results.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(import java.util.concurrent.CountDownLatch) | |
(defmacro dothreads | |
"Like (dotimes [i n] ...) except the body is executed concurrently, and the result is | |
a map from `i` to the result (or an exception if one is thrown). | |
Spins up `n-threads` threads and execute `body` in each with the thread number bound to | |
the given `tid-binding`, but use a CountDownLatch to make sure they all start at as close | |
to the same exact time as possible." | |
[[tid-binding n-threads] & body] | |
`(let [n-threads# ~n-threads | |
latch# (CountDownLatch. n-threads#)] | |
(->> | |
(for [~tid-binding (range n-threads#)] | |
(vector | |
~tid-binding | |
(future | |
(.countDown latch#) ;; Signal that we're ready | |
(.await latch#) ;; Await all other threads ready | |
~@body))) ;; Do whatever thing | |
(doall) ;; Make sure all threads start before we deref & block on their completion | |
(map (fn [[tid# task#]] [tid# (try @task# (catch Throwable t# t#))])) | |
(into {})))) | |
(comment | |
"For example..." | |
;; Execute the body in 4 different threads | |
(dothreads [n 4] | |
(let [started (System/currentTimeMillis) | |
my-name (.getName (Thread/currentThread))] | |
(println (format "My name is %s and I started at %s" my-name started)) | |
{:name my-name :started started})) | |
;; Output is mixed on the same line because they happen concurrently | |
;; My name is clojure-agent-send-off-pool-31 and I started at 1565723836090My name is clojure-agent-send-off-pool-32 and I started at 1565723836090My name is clojure-agent-send-off-pool-30 and I started at 1565723836090 | |
;; My name is clojure-agent-send-off-pool-29 and I started at 1565723836090 | |
;; Return value contains more details about how concurrent they were... | |
;; => | |
{0 {:name "clojure-agent-send-off-pool-29", :started 1565723836090}, | |
1 {:name "clojure-agent-send-off-pool-30", :started 1565723836090}, | |
2 {:name "clojure-agent-send-off-pool-31", :started 1565723836090}, | |
3 {:name "clojure-agent-send-off-pool-32", :started 1565723836090}} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment