基本上我有一个要调用的函数列表
'(f1 f2 f3 f4)
每个都是封锁的,需要不同的时间才能返回。
我想有一个懒惰的序列s,其中第一个元素是最快返回的调用的结果,调用
(first s)
只会在该呼叫的响应时间内阻止。同样适用于其他元素。
具体示例:如果
然后调用
(first s)
将阻止1并返回(f3)
的结果(into [] (take 2 s))
将阻止2s并返回(f3)和(f4)等的结果
我已经考虑过将期货中的所有功能包装起来并将结果交给承诺。 但我不知道如何理清哪个承诺将以最快的速度交付。
有没有人知道如何做到这一点?
答案 0 :(得分:3)
(require '[clojure.core.async
:refer [chan
>!
go
<!!]])
(def c (chan))
(def fns [#(do (Thread/sleep 5000) :fn-1)
#(do (Thread/sleep 2000) :fn-2)
#(do (Thread/sleep 1000) :fn-3)])
(do
(go
(doseq [f fns]
(go (>! c (f)))))
(println "First => " (<!! c)))
答案 1 :(得分:3)
使用承诺,期货和单个原子的纯粹Clojure解决方案绝对是可能的:
(defn parallelize
[fs]
(let [[h & r :as ps] (repeatedly (count fs) promise)
queue (atom (cycle ps))]
(doseq [f fs]
(future
(let [result (f)]
(-> (swap! queue rest)
(first)
(deliver result)))))
(map deref (concat r [h]))))
这基本上创建了一系列promise并使用atom将它们存储为循环队列。然后每个未来都会轮换队列,选择下一个可用的promise并传递函数结果。
示例:
(defn g
[ms]
(fn []
(Thread/sleep ms)
ms))
(doseq [value (parallelize (map g [500 200 100 300]))]
(prn value))
;; 100
;; 200
;; 300
;; 500
答案 2 :(得分:1)
如果您不想使用core.async,则可以回退到一个简单的队列:
(import 'java.util.concurrent.LinkedBlockingQueue)
(defn fut [q f] ;; this will need some error handling
(future
(.add q (f))))
(defn take-blocking [q n]
(when (pos? n)
(lazy-seq
(cons (.take q)
(take-blocking q (dec n))))))
(defn in-parallel [fns]
(let [queue (LinkedBlockingQueue. (count fns))]
(doseq [f fns]
(fut queue f))
(take-blocking queue (count fns))))
使用它:
(defn slow [n]
(fn []
(Thread/sleep (* n 1000))
n))
(doseq [r (in-parallel [(slow 5) (slow 9) (slow 1) (slow 3)])]
(println (java.util.Date.) r))
答案 3 :(得分:1)
简单并发和控制工作线程的另一个好方法是[com.climate/claypoole "0.3.3"]。它模仿map
和for
,但是并行,无论是有序还是无序,并且可以控制线程池大小(与pmap不同,其中线程池大小固定为(* 2个核心))。
以下是upmap的示例,它们是无序的并行版本的map。这意味着首先返回映射序列的最快实现版本。第一个参数是预定义的线程池,或者要使用的线程池的大小。
(require '[com.climate.claypoole :as cp]))
(defn wait-and-return
[w]
(Thread/sleep (* 1000 w))
w)
(cp/upmap 4 wait-and-return [10 5 7 9])
=> (5 7 9 10)
确保将线程池的大小调整到足以容纳最大并行等待/自定义函数的数量。
(def to-sort
(shuffle (range 0 40 2))
;not enough threads, so not returned in the right order
(def timesorted
(time (doall (cp/upmap 10 wait-and-return to-sort))))
"Elapsed time: 52001. 812056 msecs"
(apply < timesorted)
=> false
;enough threads
(def timesorted
(time (doall (cp/upmap 20 wait-and-return to-sort))))
"Elapsed time: 38002.858901 msecs"
(apply < timesorted)
=> true
期货不会遇到这些情况,因为它们的线程池会自动增加到最大值Integer / MAX_VALUE。但是,如果不是使用claypoole线程池或线程池大小来指定:builtin键,那么claypoole将使用Clojure自己的几乎无限制的线程池用于期货和代理发送。
但请注意,如果您不知道线程数量会增长多少,管理和切换所有这些线程会导致性能降低,因此您应该只在IO绑定情况下使用它,而不是在CPU中使用它绑定的。
(def timesorted
(time (doall (cp/upmap :builtin wait-and-return to-sort))))
"Elapsed time: 38001.348402 msecs"
(apply < timesorted)
=> true