我想在Clojure中创建一些未来并在特定的线程上运行它们,以确保它们一次运行一次。这可能吗?
包装Java库并不难做到这一点,但在我这样做之前,我想确保我没有错过Clojure的做法。在Java中,我可以通过实现FutureTask
并将这些任务提交给单线程执行程序来实现。
答案 0 :(得分:2)
Clojure的future macro调用future-call函数使用a dedicated executor service。这意味着您无法控制强制执行顺序执行。
另一方面,您可以使用promise代替future
个对象和一个future
主题来顺序deliver结果。 Promise的API类似于future
提供的API。他们也有deref和realized?。
以下代码示例在后台的新线程上顺序执行子任务,而函数的立即返回结果包含对计算值的承诺。
(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
。承诺存在一个问题,即您可能无意中无法提供它们,并创建了future
和delay
无法实现的死锁方案。未来和延迟之间的差异只是工作执行的线程。对于未来,工作是在后台完成的,并且延迟工作由尝试解除它的第一个线程完成。因此,如果未来比承诺更合适,你可以总是这样做:
(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))