Concurrency 如何列出并行的阻塞调用列表,并以先到先得的方式获得结果?
基本上我有一个要调用的函数列表Concurrency 如何列出并行的阻塞调用列表,并以先到先得的方式获得结果?,concurrency,clojure,parallel-processing,Concurrency,Clojure,Parallel Processing,基本上我有一个要调用的函数列表 '(f1 f2 f3 f4) 每个都是阻塞的,需要不同的时间返回 我想要一个惰性序列s,其中第一个元素是返回调用最快的结果,调用 (first s) 将只阻塞该呼叫的响应时间。其他元素也是如此 具体例子:如果 f1需要10秒 f2需要5秒 f3需要1秒 f4需要2秒 然后打电话 (first s) 将阻塞1s并返回(f3)的结果 将阻塞2s并返回(f3)和(f4)等的结果 我曾考虑将所有功能包装在futures中,并将结果交付给promises。 但我不
'(f1 f2 f3 f4)
每个都是阻塞的,需要不同的时间返回
我想要一个惰性序列s,其中第一个元素是返回调用最快的结果,调用
(first s)
将只阻塞该呼叫的响应时间。其他元素也是如此
具体例子:如果
- f1需要10秒
- f2需要5秒
- f3需要1秒
- f4需要2秒
(first s)
将阻塞1s并返回(f3)的结果
将阻塞2s并返回(f3)和(f4)等的结果
我曾考虑将所有功能包装在futures中,并将结果交付给promises。
但我不知道如何确定哪一个承诺会最快兑现
有人知道怎么做吗?(需要“[clojure.core.async
(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)))
:参阅[陈
>!
去
!c(f)(()))
(println“First=>”(如果不想使用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))
使用承诺、未来和单个原子的纯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]))))
这基本上创建了一系列承诺,并使用一个原子将它们存储为一个循环队列。然后,每个future循环队列,选择下一个可用承诺并交付函数结果
例如:
(defn g
[ms]
(fn []
(Thread/sleep ms)
ms))
(doseq [value (parallelize (map g [500 200 100 300]))]
(prn value))
;; 100
;; 200
;; 300
;; 500
简单并发和控制工作线程的另一个很好的选择是。它模仿map
和for
,但并行,有序或无序,并控制线程池大小(与pmap不同,pmap的线程池大小固定在(*2个核心))
下面是upmap的一个示例,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)
确保线程池的大小足够大,以容纳最大数量的并行等待/io绑定函数
(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
(要排序的定义)
(洗牌(范围0 40 2))
;线程不足,因此未按正确顺序返回
(def timesorted)
(时间(doall(cp/upmap 10等待并返回排序)))
“运行时间:52001.812056毫秒”
(应用<时间排序)
=>错误
;足够的线程
(def timesorted)
(时间(doall(cp/upmap 20等待并返回排序)))
“运行时间:38002.858901毫秒”
(应用<时间排序)
=>正确
Futures不会遇到这些情况,因为它们的线程池会自动增加到整数/MAX_值的最大值。但是,如果您指定的不是claypoole线程池或线程池大小,而是:builtin键,claypoole将使用Clojure自己的几乎无限的线程池,用于Futures和代理发送
但是,请注意,如果您不知道线程数量会增长到什么程度,那么管理和切换所有这些线程可能会导致性能降低,因此您应该仅在IO限制的情况下使用它,而不是在CPU限制的情况下
(def timesorted
(time (doall (cp/upmap :builtin wait-and-return to-sort))))
"Elapsed time: 38001.348402 msecs"
(apply < timesorted)
=> true
(def timesorted
(时间(doall(cp/upmap:内置等待并返回排序)))
“运行时间:38001.348402毫秒”
(应用<时间排序)
=>正确
你能说得更具体一点吗?core.async中的fns/宏在对其余部分进行计算之前是否已加载到命名空间中?另外,你知道的阻塞是否正确吗?你可以使用take!
或(转到(我不熟悉core.async,是否没有办法使用通常的clojure并发结构来实现它?将查看是否有第二个答案。可能是代理
和发送
?
(def timesorted
(time (doall (cp/upmap :builtin wait-and-return to-sort))))
"Elapsed time: 38001.348402 msecs"
(apply < timesorted)
=> true