我在矢量中保存了许多(未评估的)表达式; [expr1 expr2 expr3 ...]
我想要做的是将每个表达式交给一个单独的线程,并等待一个返回一个值。那时我对其他线程的结果不感兴趣,并希望取消它们以节省CPU资源。
(我意识到这可能导致非确定性,因为程序的不同运行可能会导致首先评估不同的表达式。我手头有这个。)
是否有实现上述目标的标准/惯用方式?
答案 0 :(得分:5)
这是我的看法。
基本上,您必须在每个期货内部解决全局承诺,然后返回包含未来清单和已解决价值的向量,然后取消列表中的所有期货:
(defn run-and-cancel [& expr]
(let [p (promise)
run-futures (fn [& expr] [(doall (map #(future (deliver p (eval %1))) expr)) @p])
[fs res] (apply run-futures expr)]
(map future-cancel fs)
res))
答案 1 :(得分:2)
尚未达到官方发布,但core.async
看起来可能是解决问题的有趣方式 - 以及其他异步问题,非常简洁。
core.async
的leiningen咒语(目前)如下:
[org.clojure/core.async "0.1.0-SNAPSHOT"]
这里有一些代码来创建一个函数,它将占用许多耗时的函数,并阻塞直到其中一个函数返回。
(require '[clojure.core.async :refer [>!! chan alts!! thread]]))
(defn return-first [& ops]
(let [v (map vector ops (repeatedly chan))]
(doseq [[op c] v]
(thread (>!! c (op))))
(let [[value channel] (alts!! (map second v))]
value)))
;; Make sure the function returns what we expect with a simple Thread/sleep
(assert (= (return-first (fn [] (Thread/sleep 3000) 3000)
(fn [] (Thread/sleep 2000) 2000)
(fn [] (Thread/sleep 5000) 5000))
2000))
在上面的示例中:
chan
创建异步频道>!!
将值放入频道thread
在另一个帖子中执行正文alts!!
采用一个频道向量,当值出现在其中任何一个除此之外还有更多的方法,我仍然围绕它,但这里有一个演练:https://github.com/clojure/core.async/blob/master/examples/walkthrough.clj
大卫·诺伦的博客上有一些很棒的,如果令人难以置信的帖子(http://swannodette.github.io/)
修改强>
刚看到MichałMarczyk回答了一个非常相似的问题,但更好的是,和它可以让你取消/短路。 with Clojure threading long running processes and comparing their returns
答案 2 :(得分:1)
你想要的是Java的CompletionService。我不知道在clojure中有任何包装,但是使用interop并不难。下面的示例基于ExecutorCompletionService的JavaDoc页面上的示例。
(defn f [col]
(let [cs (ExecutorCompletionService. (Executors/newCachedThreadPool))
futures (map #(.submit cs %) col)
result (.get (.take cs))]
(map #(.cancel % true) futures)
result))
答案 3 :(得分:0)
您可以使用future-call获取所有期货的清单,并将其存储在Atom中。然后,用一个"组成每个运行的未来;射击其他人的头部"函数,所以第一个将终止所有剩余的。 Here is an example:
(defn first-out [& fns]
(let [fs (atom [])
terminate (fn [] (println "cancling..") (doall (map future-cancel @fs)))]
(reset! fs (doall (map (fn [x] (future-call #((x) (terminate)))) fns)))))
(defn wait-for [n s]
(fn [] (print "start...") (flush) (Thread/sleep n) (print s) (flush)))
(first-out (wait-for 1000 "long") (wait-for 500 "short"))
修改强>
注意到前面的代码没有返回第一个结果,所以它主要用于副作用。这里是another version,它使用promise返回第一个结果:
(defn first-out [& fns]
(let [fs (atom [])
ret (promise)
terminate (fn [x] (println "cancling.." )
(doall (map future-cancel @fs))
(deliver ret x))]
(reset! fs (doall (map (fn [x] (future-call #(terminate (x)))) fns)))
@ret))
(defn wait-for [n s]
"this time, return the value"
(fn [] (print "start...") (flush) (Thread/sleep n) (print s) (flush) s))
(first-out (wait-for 1000 "long") (wait-for 500 "short"))
答案 4 :(得分:-1)
虽然我不知道是否有一种惯用的方式来实现你的目标,但Clojure Future看起来很合适。
采用一组表达式并产生一个未来的对象 在另一个线程中调用body,并将缓存结果和 在所有后续调用deref / @时返回它。如果计算有 尚未完成,调用deref / @将阻止,除非变种 使用deref with timeout。