並行処理によって処理時間を短縮するコードを書こうとしていた時に学んだ(ハマった)ことをだらだら書きます。最終的にはpmapを使えばいい話でした。futureを使って並列実行させようとしても直列実行しかしてくれなかった経験です。
最初に書いた関数の構造はこんな感じ。n個のパラメータをrangeで作って、m個の配列に分割して、mスレッドそれぞれでループさせようというもの。
(defn test [] (def all-params (map #(apply str (concat [ "param-" (str %) ]))(range 1 8))) (def partitioned-params (partition 2 2 nil all-params)) (reduce into [] (map deref (map (fn [x] (future (reduce into [] (map (fn [y] (println "(" y ")" )(Thread/sleep 1000)) x) ))) partitioned-params))))
これを実行すると(param-1), ..-3, ..-5,..-7は同時に表示してほしいところが1秒ごとの表示できれいに直列呼び出しなってしまいます。
clj-lib.core=> (test) ( param-1 ) ( param-2 ) ( param-3 ) ( param-4 ) ( param-5 ) ( param-6 ) ( param-7 ) []
悪かったところは、内側のmapに渡しているcollectionがlazy-seqであるところで、なので
(def partitioned-params (vec (partition 2 2 nil all-params)))
のようにすると意図した通り動いてくれました。最初mapの戻り値がlazy-seqなのが悪いのかとdoall挟んでみたりしていましたが、こっちだっとは!
でもってさらに最終的にはpmapを使えば、collectionをvecにする必要も、明示的にfutureとderefを書く必要もなかったです。
以上でした。