2017-12-27 1418 views
0

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

将Java库包装起来并不难,但在这之前我想确保我不会错过Clojure的做法。在Java中,我可以通过执行FutureTask并将这些任务提交给单线程执行程序来执行此操作。

+0

你为什么要创造出设计用于并发性,不仅是执行,他们执行的非同时构建?也许你以这种方式定义了现有的任务,或者你通常同时执行它们,但是想在有限的时间内强制顺序执行?只是好奇。 – Josh

+0

假设有多个线程(例如,服务器中的请求线程)都希望运行使用共享资源X的任务,而共享资源X一次只能由一个线程使用。一种解决方案是在访问X时获取锁。另一种方法是提交任务(期货)以在与X关联的单个线程上运行。其他调用线程可能会或可能不会等待未来的结果。 –

+0

当然,但为什么在这种情况下创建线程呢?为什么不把这些任务作为一个线程的函数来调用呢? – Josh

回答

2

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

在另一方面,你可以使用的promise代替future对象,一个future线程顺序deliver结果。 Promise的API与future提供的相似。他们也有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

,如果你想sequentialize到future来电您可以手动使用它像这样:

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

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

你可以用宏美化这样的:

(defmacro sequentialize [& futures] 
    `(do [email protected](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 
0

Manifold提供一种方式来创造未来with specific executor。它不是核心Clojure库的一部分,但它仍然是一个高质量的库,如果你需要更多的灵活性来处理未来,而不是核心lib提供的(而不是采用Java interop),那么这可能是一个最好的选择。

0

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

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