Clojure的平行剂量

and*_*oke 16 parallel-processing multithreading clojure

我根本没有在Clojure中使用多线程,所以不确定从哪里开始.

doseq的身体可以并行运行.我想要的是总是有3个线程运行(留下1个核心空闲),并行地评估主体直到范围耗尽.没有共享状态,没有什么复杂的 - 相当于Python的多处理就可以了.

所以类似于:

(dopar 3 [i (range 100)]
  ; repeated 100 times in 3 parallel threads...
  ...)
Run Code Online (Sandbox Code Playgroud)

我应该从哪里开始寻找?有这个命令吗?标准包装?一个很好的参考?

到目前为止,我已经找到了pmap,并且可以使用它(我如何一次限制为3?看起来它一次使用32个 - 不,源说2 +处理器数量),但似乎这是一个基本原语应该已经存在于某处.

澄清:我真的想控制线程的数量.我有长时间运行的进程并使用相当数量的内存,所以创建一个大数字并希望事情正常运行OK不是一个好方法(例如使用重要的块可用内存).

更新:开始写一个执行此操作的宏,我需要一个信号量(或一个互斥量,或者我可以等待的原子).Clojure中是否存在信号量?或者我应该使用ThreadPoolExecutor?从Java中提取这么多内容似乎很奇怪 - 我认为Clojure中的并行编程应该很容易......也许我正在考虑这完全错误的方式?嗯.代理?

and*_*oke 6

好吧,我认为我想要的是agent每个循环都有一个,使用发送给代理的数据send.触发使用的代理程序send是从一个线程池运行的,所以这个数字在某种程度上是有限的(它不会给出正好三个线程的细粒度控制,但它现在必须要做).

[Dave Ray在评论中解释:控制池大小我需要自己编写]

(defmacro dopar [seq-expr & body]
  (assert (= 2 (count seq-expr)) "single pair of forms in sequence expression")
  (let [[k v] seq-expr]
    `(apply await
       (for [k# ~v]
         (let [a# (agent k#)]
           (send a# (fn [~k] ~@body))
         a#)))))
Run Code Online (Sandbox Code Playgroud)

可以使用如下:

(deftest test-dump
  (dopar [n (range 7 11)]
    (time (do-dump-single "/tmp/single" "a" n 10000000))))
Run Code Online (Sandbox Code Playgroud)

好极了!作品!我好棒!(好吧,Clojure也有点摇晃). 相关博文.


mik*_*era 5

pmap在大多数情况下实际上可以正常工作 - 它使用一个线程池,为您的机器提供合理数量的线程。我不会费心去尝试创建自己的机制来控制线程数,除非您有真实的基准证据表明默认值会导致问题。

话虽如此,如果你真的想限制最多三个线程,一个简单的方法是只在范围的 3 个子集上使用 pmap:

(defn split-equally [num coll] 
  "Split a collection into a vector of (as close as possible) equally sized parts"
  (loop [num num 
         parts []
         coll coll
         c (count coll)]
    (if (<= num 0)
      parts
      (let [t (quot (+ c num -1) num)]
        (recur (dec num) (conj parts (take t coll)) (drop t coll) (- c t)))))) 

(defmacro dopar [thread-count [sym coll] & body]
 `(doall (pmap 
    (fn [vals#]
      (doseq [~sym vals#]
        ~@body))  
    (split-equally ~thread-count ~coll))))
Run Code Online (Sandbox Code Playgroud)

请注意使用doall,这是强制评估pmap(这是惰性的)所必需的。

  • 如果您不想保留对 head 的引用(更少的内存使用),则 `dorun` 是一个比 `doall` 更好的选择 (2认同)

div*_*210 5

现在实际上有一个图书馆可以做到这一点。从他们的github

所述claypoole库提供的功能的Clojure基于线程池并行版本,如pmapfuture,和for

它提供了相同的有序/无序版本。