Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Future vs Thread: Which is better for working with channels in core.async?

When working with channels, is future recommended or is thread? Are there times when future makes more sense?

Rich Hickey's blog post on core.async recommends using thread rather than future:

While you can use these operations on threads created with e.g. future, there is also a macro, thread , analogous to go, that will launch a first-class thread and similarly return a channel, and should be preferred over future for channel work.

~ http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html

However, a core.async example makes extensive use of future when working with channels:

(defn fake-search [kind]
  (fn [c query]
    (future
     (<!! (timeout (rand-int 100)))
     (>!! c [kind query]))))

~ https://github.com/clojure/core.async/blob/master/examples/ex-async.clj

like image 377
gilmaso Avatar asked Dec 17 '13 03:12

gilmaso


3 Answers

Summary

In general, thread with its channel return will likely be more convenient for the parts of your application where channels are prominent. On the other hand, any subsystems in your application that interface with some channels at their boundaries but don't use core.async internally should feel free to launch threads in whichever way makes the most sense for them.

Differences between thread and future

As pointed out in the fragment of the core.async blog post you quote, thread returns a channel, just like go:

(let [c (thread :foo)]
  (<!! c))
;= :foo

The channel is backed by a buffer of size 1 and will be closed after the value returned by the body of the thread form is put on it. (Except if the returned value happens to be nil, in which case the channel will be closed without anything being put on it -- core.async channels do not accept nil.)

This makes thread fit in nicely with the rest of core.async. In particular, it means that go + the single-bang ops and thread + the double-bang ops really are used in the same way in terms of code structure, you can use the returned channel in alt! / alts! (and the double-bang equivalents) and so forth.

In contrast, the return of future can be deref'd (@) to obtain the value returned by the future form's body (possibly nil). This makes future fit in very well with regular Clojure code not using channels.

There's another difference in the thread pool being used -- thread uses a core.async-specific thread pool, while future uses one of the Agent-backing pools.

Of course all the double-bang ops, as well as put! and take!, work just fine regardless of the way in which the thread they are called from was started.

like image 161
Michał Marczyk Avatar answered Nov 19 '22 14:11

Michał Marczyk


it sounds like he is recommending using core. async's built in thread macro rather than java's Thread class.

http://clojure.github.io/core.async/#clojure.core.async/thread

like image 35
Arthur Ulfeldt Avatar answered Nov 19 '22 13:11

Arthur Ulfeldt


Aside from which threadpool things are run in (as pointed out in another answer), the main difference between async/thread and future is this:

  • thread will return a channel which only lets you take! from the channel once before you just get nil, so good if you need channel semantics, but not ideal if you want to use that result over and over
  • in contrast, future returns a dereffable object, which once the thread is complete will return the answer every time you deref , making it convenient when you want to get this result more than once, but this comes at the cost of channel semantics

If you want to preserve channel semantics, you can use async/thread and place the result on (and return a) async/promise-chan, which, once there's a value, will always return that value on later take!s. It's slightly more work than just calling future, since you have to explicitly place the result on the promise-chan and return it instead of the thread channel, but buys you interoperability with the rest of the core.async infrastructure.

It almost makes one wonder if there shouldn't be a core.async/thread-promise and core.async/go-promise to make this more convenient...

like image 1
metasoarous Avatar answered Nov 19 '22 15:11

metasoarous