Reputation: 16439
I'd like to create some futures in Clojure and run them all on a specific thread, to make sure they run one-at-a-time. Is this possible?
It's not hard to wrap the Java libraries to do this, but before I do that I want to make sure I'm not missing a Clojure way of doing it. In Java I can do this by implementing FutureTask
and submitting those tasks to a single-threaded executor.
Upvotes: 1
Views: 1132
Reputation: 2968
In addition the promises
mentioned, you can use a delay
. Promises have the problem that you can accidentally not deliver them, and create a deadlock scenario that's not possible with future
s and delay
s. The difference between a future and a delay is only the thread that the work is executed on. With a future, the work is done in the background, and with a delay the work is done by the first thread that tries to deref it. So if future's are a better fit than promises, you could always do something like:
(def result-1 (delay (long-calculation-1)))
(def result-2 (delay (long-calculation-2)))
(def result-3 (delay (long-calculation-3)))
(defn run-calcs []
@(future
@result-1
@result-2
@result-3))
Upvotes: 0
Reputation: 308
Manifold provides a way to create future with specific executor. It's not part of core Clojure lib, but it's still a high quality lib and probably a best option in case you need more flexibility dealing with futures than core lib provides (without resorting to Java interop).
Upvotes: 0
Reputation: 17849
if you want to sequentialize the calls to future
you can use it manually like this:
(do @(future 1)
@(future 2)
@(future 3))
they would still possibly called in different threads, but the next one won't be called until the previous has finished. This is guaranteed by the @
(or deref
function). This means that the thread in which you execute do
form would be blocked with prev promise before it completes, and then spawn next one.
you can prettify it with macro like this:
(defmacro sequentialize [& futures]
`(do ~@(map #(list `deref %) futures)))
user> (let [a (atom 1)]
(sequentialize
(future (swap! a #(* 10 %)))
(future (swap! a #(+ 20 %)))
(future (swap! a #(- %))))
@a)
;;=> -30
this does exactly the same as manual do
. Notice that mutations to a
atom are in-order even if some threads run longer:
user> (let [a (atom 1)]
(sequentialize
(future (Thread/sleep 100)
(swap! a #(* 10 %)))
(future (Thread/sleep 200)
(swap! a #(+ 20 %)))
(future (swap! a #(- %))))
@a)
;;=> -30
Upvotes: 1
Reputation: 3538
Clojure's future macro calls future-call function which uses a dedicated executor service. This means that you have no control to enforce a sequential execution.
On the other hand you can use promise instead of future
objects and one future
thread to sequentially deliver the results. Promise's API is similar to what future
s provide. They have deref and realized? too.
The following code example has the subtasks executed sequentially on a new thread in the background while the immediately returned result of the function contains the promises to the computed values.
(defn start-async-calc []
(let [f1 (promise)
f2 (promise)
f3 (promise)]
(future
(deliver f1 (task-1))
(deliver f2 (task-2))
(deliver f3 (task-3)))
{:task1 f1
:task2 f2
:task3 f3}))
Upvotes: 2