如何在单个线程中执行一些Clojure期货?

时间:2017-12-27 20:23:24

标签: multithreading clojure future

我想在Clojure中创建一些未来并在特定的线程上运行它们,以确保它们一次运行一次。这可能吗?

包装Java库并不难做到这一点,但在我这样做之前,我想确保我没有错过Clojure的做法。在Java中,我可以通过实现FutureTask并将这些任务提交给单线程执行程序来实现。

4 个答案:

答案 0 :(得分:2)

Clojure的future macro调用future-call函数使用a dedicated executor service。这意味着您无法控制强制执行顺序执行。

另一方面,您可以使用promise代替future个对象和一个future主题来顺序deliver结果。 Promise的API类似于future提供的API。他们也有derefrealized?

以下代码示例在后台的新线程上顺序执行子任务,而函数的立即返回结果包含对计算值的承诺。

(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}))

答案 1 :(得分:1)

如果要将调用序列化为future,可以像下面这样手动使用它:

(do @(future 1)
    @(future 2)
    @(future 3))

他们仍然可能在不同的线程中调用,但是直到前一个完成后才会调用下一个。这由@(或deref函数)保证。这意味着执行do表单的线程在完成之前将被使用prev promise阻止,然后生成下一个。

你可以像这样用宏来美化它:

(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

这与手动do完全相同。请注意,即使某些线程运行时间更长,a原子的突变也是有序的:

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

答案 2 :(得分:0)

Manifold提供了一种创建未来with specific executor的方法。它不是核心Clojure库的一部分,但它仍然是一个高质量的库,如果你需要更多的灵活性处理期货而不是核心库提供的话,它可能是最好的选择(不需要借助Java互操作)。 / p>

答案 3 :(得分:0)

除了提到的promises之外,您还可以使用delay。承诺存在一个问题,即您可能无意中无法提供它们,并创建了futuredelay无法实现的死锁方案。未来和延迟之间的差异只是工作执行的线程。对于未来,工作是在后台完成的,并且延迟工作由尝试解除它的第一个线程完成。因此,如果未来比承诺更合适,你可以总是这样做:

(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))