多线程 – 为什么在调用期货时只运行32个线程?

当我发现期货经营测试时:
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个任务的每个片段,我们再看一次.

制定未来的代码

(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)))

未来电话中有趣的一点是

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

其转换为:

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

所以这确实创造了无限制的线程池.

那么为什么只有32个线程运行而不是创建1000个线程并返回一秒钟?

解决方法

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

实际上应该需要65秒,因为你要求在主线程上依次退出期货.这些期货甚至还没有开始,因为这是因为是懒惰的.你得到32的倍数的原因是范围的分块行为.

与非分块版本进行比较

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

正如Rörd在评论中所指出的,增加一个方法来消除未来创作的懒惰,解决了这个问题.

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

测试所有期货完成的另一种方法是等待上一个未来完成之前不会交付的承诺.

(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

相关文章

最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤...
/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不...
/*list 基本操作 * * List a=new List(); * 增 * a.add(inde...
/* * 内部类 * */ 1 class OutClass{ 2 //定义外部类的成员变...
集合的操作Iterator、Collection、Set和HashSet关系Iterator...
接口中常量的修饰关键字:public,static,final(常量)函数...