理解Clojure期货

理解Clojure期货,clojure,future,Clojure,Future,我试图理解Clojure futures,我看到了常见的Clojure书籍中的例子,也有一些例子将futures用于并行计算(这似乎是有道理的) 然而,我希望有人能解释一个简单例子的行为,这个例子改编自O'Reilly的编程Clojure书 (def long-calculation (future (apply + (range 1e8)))) 当我试图去引用它时,通过 (time @long-calculation) 它返回正确的结果(499999950000000),但在我的机器上几乎

我试图理解Clojure futures,我看到了常见的Clojure书籍中的例子,也有一些例子将futures用于并行计算(这似乎是有道理的)

然而,我希望有人能解释一个简单例子的行为,这个例子改编自O'Reilly的编程Clojure书

(def long-calculation (future (apply + (range 1e8))))
当我试图去引用它时,通过

(time @long-calculation)
它返回正确的结果(499999950000000),但在我的机器上几乎立即返回(0.045毫秒)

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

(time (apply + (range 1e8)))
我也得到了正确的结果,但所花费的时间要大得多(约5000毫秒)

当我取消对future的引用时,我的理解是创建了一个新线程,在该线程上对表达式进行求值——在这种情况下,我希望它也需要大约5000毫秒


为什么取消引用的future会如此快地返回正确的结果?

一旦创建future(在单独的线程中),future中的计算就会立即开始。在您的情况下,只要执行
(def long calculation…)

取消引用将执行以下两项操作之一:

  • 如果future尚未完成,则阻塞直到完成,然后返回值(这可能需要任意时间,如果future未能终止,甚至永远不会完成)
  • 如果未来已完成,请返回结果。这几乎是瞬间的(这就是为什么你会看到非常快的解引用返回)
通过比较以下各项,您可以看到效果:

;; 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"

使用大量期货是否有缺点?我一直在写一些代码,在很多地方进行数值密集型计算。我可以不使用本机Java数组或进行类型暗示,而只编写惯用的函数代码和
future
这些计算的结果吗?Futures相当轻量级,但确实有一些开销,因此我会避免在非常小的计算中使用它们。如果您想并行计算,请考虑使用<代码> PMAP——这是一个并发版本的<代码> MAP>代码>,它在引擎盖下使用期货。话虽如此,如果你的代码确实是数字密集型的,那么如果你想充分利用你的CPU时间,你可能最好同时使用Java数组和pmap/futures。我曾经尝试过使用pmap,但发现它只有在数据大小“大”的情况下才有用(当然,多大有些主观)。我一直在通过实现一些简单的数字信号处理函数来学习Clojure,与单处理器/线程模式下带有seq抽象的函数样式相比,使用本机Java数组具有明显的速度优势。如果我使用futures,那么速度将是如此之快,以至于无论我使用的是本机代码还是函数代码都无关紧要。感觉我错过了一些明显的东西。