如何在单线程中执行一些 Clojure 期货?
How to execute some Clojure futures in a single thread?
我想在 Clojure 中创建一些 futures 并 运行 它们都在特定线程上,以确保它们一次 运行 一个。这可能吗?
包装 Java 库来执行此操作并不难,但在我这样做之前,我想确保我没有错过 Clojure 的实现方式。在 Java 中,我可以通过实现 FutureTask
并将这些任务提交给单线程执行程序来实现。
Clojure 的 future macro calls future-call function which uses a dedicated executor service。这意味着您无法控制强制顺序执行。
另一方面,您也可以使用 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?。
以下代码示例在后台的新线程上按顺序执行子任务,而函数立即返回的结果包含对计算值的承诺。
(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}))
如果您想按顺序调用 future
,您可以像这样手动使用它:
(do @(future 1)
@(future 2)
@(future 3))
它们仍然可能在不同的线程中调用,但下一个线程不会在前一个线程完成之前被调用。这是由 @
(或 deref
函数)保证的。这意味着您执行 do
表单的线程将在完成之前被上一个承诺阻塞,然后产生下一个。
你可以用这样的宏来美化它:
(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
Manifold provides a way to create future with specific executor。它不是核心 Clojure 库的一部分,但它仍然是一个高质量的库,如果您需要比核心库提供的更多灵活性来处理未来(不求助于 Java 互操作),它可能是最佳选择。
除了promises
提到的,还可以使用delay
。 Promises 有一个问题,您可能会意外地不交付它们,并创建一个 future
s 和 delay
s 不可能发生的死锁场景。未来和延迟之间的区别仅在于执行工作的线程。有了 future,工作在后台完成,延迟后,工作由第一个尝试取消引用它的线程完成。所以如果 future 比 promises 更合适,你总是可以这样做:
(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))
我想在 Clojure 中创建一些 futures 并 运行 它们都在特定线程上,以确保它们一次 运行 一个。这可能吗?
包装 Java 库来执行此操作并不难,但在我这样做之前,我想确保我没有错过 Clojure 的实现方式。在 Java 中,我可以通过实现 FutureTask
并将这些任务提交给单线程执行程序来实现。
Clojure 的 future macro calls future-call function which uses a dedicated executor service。这意味着您无法控制强制顺序执行。
另一方面,您也可以使用 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?。
以下代码示例在后台的新线程上按顺序执行子任务,而函数立即返回的结果包含对计算值的承诺。
(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}))
如果您想按顺序调用 future
,您可以像这样手动使用它:
(do @(future 1)
@(future 2)
@(future 3))
它们仍然可能在不同的线程中调用,但下一个线程不会在前一个线程完成之前被调用。这是由 @
(或 deref
函数)保证的。这意味着您执行 do
表单的线程将在完成之前被上一个承诺阻塞,然后产生下一个。
你可以用这样的宏来美化它:
(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
Manifold provides a way to create future with specific executor。它不是核心 Clojure 库的一部分,但它仍然是一个高质量的库,如果您需要比核心库提供的更多灵活性来处理未来(不求助于 Java 互操作),它可能是最佳选择。
除了promises
提到的,还可以使用delay
。 Promises 有一个问题,您可能会意外地不交付它们,并创建一个 future
s 和 delay
s 不可能发生的死锁场景。未来和延迟之间的区别仅在于执行工作的线程。有了 future,工作在后台完成,延迟后,工作由第一个尝试取消引用它的线程完成。所以如果 future 比 promises 更合适,你总是可以这样做:
(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))