下面是我正在处理的应用程序的简化版本。具体来说,我对基准process-list
的执行时间感兴趣。在函数process-list
中,我将输入列表划分为多个分区,这些分区等于我要并行执行的线程数。然后,我通过调用future
将每个分区传递给线程。最后,在main
中,我将process-list
包裹在time
周围。时间应该返回由process-list
完成的处理时间,但显然,它仅返回创建将来线程所花费的时间,而不必等待将来执行完成。如何在process-list
内取消对期货的引用,以确保所用的时间占期货线程执行完成的时间?
(ns listProcessing
(:require [clojure.string]
[clojure.pprint]
[input-random :as input]))
(def N-THREADS 4)
(def element_processing_retries (atom 0))
(def list-collection
"Each element is made into a ref"
(map ref input/myList))
(defn partition-list [threads list]
"partition list into required number of partitions which is equal
to the number of threads"
(let [partitions (partition-all
(Math/ceil (/ (count list) threads)) list)]
partitions))
(defn increase-element [element]
(ref-set element inc))
(defn process-list [list]
"Process `members of list` one by one."
(let [sub-lists (partition-list N-THREADS list)]
(doseq [sub-list sub-lists]
(let [futures '()
myFuture (future (dosync (swap! element_processing_retries inc)
(map increase-element sub-list)))]
(cons myFuture futures)
(map deref futures)))))
(defn main []
(let [f1 (future (time (process-list input/mylist)))]
@f1)
(main)
(shutdown-agents)
下面是简化列表输入的一个示例:请注意,此处的输入是简化的,列表处理也可以简化问题。
(ns input-random)
(def myList (list 1 2 4 7 89 12 34 45 56))
答案 0 :(得分:1)
这会有一些开销。如果您尝试的是time
毫秒差异,则可能会有些偏差(尽管分钟的时间绝对不应该使用time
)。
我认为您的示例有些复杂,因此我将其简化为我认为代表问题的示例:
(time (doseq [n (range 5)]
(future
(Thread/sleep 2000))))
"Elapsed time: 1.687702 msecs"
这里的问题与您的代码中的问题相同:真正要做的只是时间doseq
分配所有作业所花费的时间。
我的想法是将每个完成的任务放到一个原子中,然后在繁忙的等待中检查结束条件:
(defn do-stuff [n-things]
(let [ret-atom (atom 0)]
(doseq [n (range n-things)]
(future
(Thread/sleep 2000)
(swap! ret-atom inc)))
ret-atom))
; Time how long it takes the entire `let` to run
(time
(let [n 5
ret-atom (do-stuff n)]
; Will block until the condition is met
(while (< @ret-atom n))))
"Elapsed time: 2002.813288 msecs"
您很难做的原因就是您正在doseq
中产生一些副作用。没有什么定义“完成”是什么,因此没有什么可以阻止的。我对core.async
不太满意,但我怀疑其中可能会有帮助。可能会阻塞对<!!
的调用,直到某个通道具有一定数量的元素为止。在这种情况下,您只需要在结果生成时将其放入渠道即可。