是否有一个Clojure成语用于并行调度多个表达式

时间:2013-08-19 13:28:57

标签: concurrency clojure

我在矢量中保存了许多(未评估的)表达式; [expr1 expr2 expr3 ...]

我想要做的是将每个表达式交给一个单独的线程,并等待一个返回一个值。那时我对其他线程的结果不感兴趣,并希望取消它们以节省CPU资源。

(我意识到这可能导致非确定性,因为程序的不同运行可能会导致首先评估不同的表达式。我手头有这个。)

是否有实现上述目标的标准/惯用方式?

5 个答案:

答案 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。