Threads, Promises, and Futures
Info
Clojure functions implement Java’s Runnable
interface. Clojure vars live in thread-local storage.
Create a thread, execute a function in it, and wait for it to finish:
(defn hello-goodbye []
(println "hello")
(println "goodbye"))
(def thread (Thread. hello-goodbye))
(.start thread)
(.join thread)
;; hello
;; goodbyeSame, but with multiple threads and parametrized output functions for distinction:
(defn hello-goodbye-fn [name]
(fn []
(println "hello from" name)
(Thread/sleep 1000)
(println "goodbye from" name)))
(def thread-a (Thread. (hello-goodbye-fn "a")))
(def thread-b (Thread. (hello-goodbye-fn "b")))
(.start thread-a)
(.start thread-b)
(.join thread-a)
(.join thread-b)
;; hello from a
;; hello from b
;; goodbye from a
;; goodbye from bSame, but with many threads:
(def threads (map #(Thread. (hello-goodbye-fn (str %))) (take 5 (iterate inc 1))))
(map (memfn start) threads)
(map (memfn join) threads)Deliver a result through a promise:
(def result (promise))
(deliver result 42)
(deref result) ; 42
@result ; 42@result is syntactic sugar for (deref result). A value can only be
delivered once, but derefed multiple times.
(deliver result 99)
@result ; still 42Use a promise to communicate between threads:
(defn sum-prom [from to]
(let [n (inc (- to from))
result (promise)
thread (Thread. #(deliver result (reduce + (take n (iterate inc from)))))]
(.start thread)
result))
(deref (sum-from-to 1 1e6)) ; 500000500000
@(sum-prom 1 1e6) ; 500000500000Simplify concurrent code using a future, which is a promise with its own thread:
(defn sum-fut [from to]
(let [n (inc (- to from))
f (future (reduce + (take n (iterate inc from))))]
f))
(deref (sum-fut 1 1e6)) ; 500000500000
@(sum-fut 1 1e6) ; 500000500000Use a timeout to deliver a fallback value if a computation takes too long:
(defn sum-from-to [from to]
(let [n (inc (- to from))]
(reduce + (take n (iterate inc from)))))
(def f (future (sum-from-to 1 1e6)))
(deref f 500 "computation took too long") ; 500000500000
(def f (future (sum-from-to 1 1e7)))
(deref f 500 "computation took too long") ; "computation took too long"Exercises
Countdown
Write a function countdown that expects a name and a number n
from which to count down to zero. Print the name and the number, which
is to be counted down, and then pause for a second. Continue printing
until the number reached zero.
Then write a function launch-rockets that expects a number n (the
number of rockets to be launched) and sec (the number of seconds
from which to count down to zero). Create one thread per rocket,
i.e. n threads, which execute the contdown function with the name
rocket X, where X is a number from 1 to n. Start those threads.
Hint: Use (locking *out* (println OUTPUT)) to make the output in
countdown thread-safe.
Test: (launch-rockets 3 4) shall produce the following (non-deterministic) output:
rocket 1: 4
rocket 2: 4
rocket 3: 4
rocket 1: 3
rocket 2: 3
rocket 3: 3
rocket 1: 2
rocket 2: 2
rocket 3: 2
rocket 1: 1
rocket 2: 1
rocket 3: 1
rocket 1: 0
rocket 3: 0
rocket 2: 0Map in Parallel
Write a function factor that performs prime factorization without
any promises or futures and calculates the prime factors
synchronuously.
Then write a function factors that accepts a sequence of numbers,
which shall be factorized using the factor function just written,
but doing so in parallel.
Hint: Integer factorization
(Wikipedia)
describes how a number can be factorized into its prime factors. Use
the solution for the Lazy Prime
Numbers exercise to get
candidates for prime factors. Use the pmap function as a concurrent
version of map.
Test: (factor (factors (take 10 (iterate inc 10))) shall return ([2 5] [11] [2 2 3] [13] [2 7] [3 5] [2 2 2 2] [17] [2 3 3] [19]).
Prime Factors Promise
Write a function factor-prom that expects a parameter x, which is
an integer to be factorized into its prime factors, and returns a
promise that delivers a sequence of that number’s prime factors. The
actual computation shall take place in its own thread.
Hint: Re-use the factor function from the last exercise.
Test: @(factor-prom 324) shall return [2 2 3 3 3 3].
Prime Factors Future
Write a function factor-fut that performs the same computation as
factor-prom from the exercise before, but returns a future instead
of a promise.
Hint: Start from factor-prom of the last exercise and simplify its
code.
Test: @(factor-fut 324) shall return [2 2 3 3 3 3].