9

先物でテストを実行すると、次のことがわかりました。

user=> (time (doall (map deref (for [i (range 1000)]
  #_=>    (future (Thread/sleep 1000))))))
"Elapsed time: 32058.208 msecs"

2 の累乗が表示されると、頭の中で警鐘が鳴っています。これは、32 個のスレッドしか開始されていないようなにおいがします。

いくつかのターゲットを絞った実験:

user=> (time (doall (map deref (for [i (range 32)]
  #_=> (future (Thread/sleep 1000))))))
"Elapsed time: 1002.302 msecs"
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)
user=> (time (doall (map deref (for [i (range 64)]
  #_=> (future (Thread/sleep 1000))))))
"Elapsed time: 2004.24 msecs"
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)
user=> (time (doall (map deref (for [i (range 65)]
  #_=> (future (Thread/sleep 1000))))))
"Elapsed time: 3005.279 msecs"
(nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil     nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)

これを確認します。32 個の将来のタスクのスライスごとに 1 秒追加されます。

未来を作るコードは

(defmacro future
  "Takes a body of expressions and yields a future object that will
  invoke the body in another thread, and will cache the result and
  return it on all subsequent calls to deref/@. If the computation has
  not yet finished, calls to deref/@ will block, unless the variant of
  deref with timeout is used. See also - realized?."
  {:added "1.1"}
  [& body] `(future-call (^{:once true} fn* [] ~@body)))

future-call の興味深い部分は

    fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]

これは次のように変換されます。

volatile public static ExecutorService soloExecutor = Executors.newCachedThreadPool(
    createThreadFactory("clojure-agent-send-off-pool-%d", sendOffThreadPoolCounter));

したがって、これは実際に無制限のスレッドプールを作成します。

では、1000 個のスレッドを作成して 1 秒で返すのではなく、32 個のスレッドしか実行していないのはなぜでしょうか?

4

1 に答える 1

12

これ

(time (doall (map deref (for [i (range 65)] (future (Thread/sleep 1000))))))
; "Elapsed time: 3005.244983 msecs"

メインスレッドで先物を順番に逆参照するように要求しているため、実際には65秒かかるはずです。deref が来ても、これらの先物は開始されていませんfor。32 の倍数を取得する理由は、のチャンク動作ですrange

非チャンク版との比較

 (time (doall (map deref (for [i (apply list (range 65))] (future (Thread/sleep 1000))))))
; "Elapsed time: 64997.260808 msecs"

コメントでRördが指摘したように、別のものを追加してdoall、将来の作成の怠惰を取り除くと、問題が解決します。

(time (doall (map deref (doall (for [i (range 65)] (future (Thread/sleep 1000)))))))
; "Elapsed time: 999.263631 msecs"

すべての Future の完了をテストする別の方法は、最後の Future が完了するまで Promise が配信されないのを待つことです。

(defn test-futures [n]
  (let [a (atom 0)
        p (promise)
        f (fn [] (swap! a inc) (when (= @a n) (deliver p n)))]
    (doseq [i (range n)] (future (do (Thread/sleep 1000) (f))))
    (deref p))) 

そして、これらの先物の 64、65、または 1000 の完了がそれぞれ約 1 秒で発生することがわかります。

(time (test-futures 64))
; "Elapsed time: 996.262272 msecs"
; 64
(time (test-futures 65))
; "Elapsed time: 996.554436 msecs"
; 65
(time (test-futures 1000))
; "Elapsed time: 1000.247374 msecs"
; 1000
于 2013-09-23T16:39:01.393 に答える