Frage

I'm learning about concurrency in Clojure.

I ran into a claim (by Stuart Serra?) at http://dev.clojure.org/display/design/Scheduled+Events, stating:

  • Clojure functions cannot use time for control flow without blocking or Java interop
  • Java interop (ScheduledThreadPoolExecutor) is not aware of thread-local bindings

I don't understand these claims and kindly ask for clarification, perhaps an example. Specifically:

  • What's wrong with ScheduledThreadPoolExecutor as is? Since I'm starting a new (green) thread, I don't expect per-thread bindings to carry over anyway.
  • I can schedule a normal Clojure function, so what's stopping me to send desired bindings as lexically closed context?

Many thanks!

War es hilfreich?

Lösung

Ok, I think I got it.

Suppose you try this:

(def pool (atom nil))

(defn- thread-pool []
    (or @pool
        (reset! pool (ScheduledThreadPoolExecutor. 1))))

(def ^:dynamic *t* 0)

(binding [*t* 1]
    (future (println "first example:" *t*)))

(binding [*t* 1]
    (.schedule (thread-pool) (fn [] (println "second example:" *t*)) 0 
               TimeUnit/SECONDS))

(binding [*t* 1]
    (.schedule (thread-pool) (bound-fn [] (println "third example:" *t*)) 0
               TimeUnit/SECONDS))

The output will be:

first example: 1
second example: 0
third example: 1

In the first case, the future macro wraps the body with the private function binding-conveyor-fn, which preserves the calling thread's bindings frame in the lexical scope, and restores it before calling the wrapped function.

In the third case, bound-fn pushes the calling thread's bindings onto the frame, executes the function body, and pops the bindings.

In the second case, no one saves per-thread bindings - a Java class certainly doesn't know about them, and so we drop to the root value of the t Var.

I hope someone out there finds this interesting.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top