了解Clojure期货

end*_*gin 7 future clojure

我试图理解Clojure期货,我已经看到了Clojure书中的常见例子,并且有一些例子将期货用于并行计算(这似乎是有道理的).

但是,我希望有人可以解释一个改编自O'Reilly编程Clojure书的简单例子的行为.

(def long-calculation (future (apply + (range 1e8))))
Run Code Online (Sandbox Code Playgroud)

当我尝试取消引用时,通过这样做

(time @long-calculation)
Run Code Online (Sandbox Code Playgroud)

它返回正确的结果(4999999950000000),但几乎立即(在0.045毫秒)我的机器上.

但是当我调用实际函数时,就像这样

(time (apply + (range 1e8)))
Run Code Online (Sandbox Code Playgroud)

我也得到了正确的结果,但所花费的时间要大得多(~5000毫秒).

当我取消引用未来时,我的理解是创建了一个新的线程,在该线程上评估表达式 - 在这种情况下,我预计它也需要大约5000毫秒.

为什么解除引用的未来如此迅速地返回正确的结果?

mik*_*era 11

一旦您创建未来(在单独的线程中),将来的计算就会开始.在您的情况下,计算一执行就开始计算(def long-calculation ....)

解除引用将执行以下两项操作之一:

  • 如果未来还没有完成,阻塞直到它完成然后返回值(这可能需要任意的时间,如果将来未能终止,甚至永远不会完成)
  • 如果未来已完成,请返回结果.这几乎是瞬间的(这就是为什么你看到非常快速的dereference返回)

您可以通过比较以下内容来查看效果:

;; dereference before future completes
(let [f (future (Thread/sleep 1000))]
  (time @f))
=> "Elapsed time: 999.46176 msecs"

;; dereference after future completes
(let [f (future (Thread/sleep 1000))]
  (Thread/sleep 2000)
  (time @f))
=> "Elapsed time: 0.039598 msecs"
Run Code Online (Sandbox Code Playgroud)

  • 期货相当轻量级但确实有一些开销,所以我会避免使用它们进行极小的计算.如果你想并行进行计算,可以考虑使用`pmap` - 这是一个使用期货的`map`的并发版本.话虽如此,如果您的代码确实是数字密集型的,那么如果您希望最好地利用CPU时间,那么最好使用Java数组*和*pmap/future. (2认同)