futures 和 promises 都会阻塞,直到它们计算出它们的值,那么它们之间有什么区别呢?
6 回答
用 Clojure 术语回答,以下是Sean Devlin 截屏视频中的一些示例:
(def a-promise (promise))
(deliver a-promise :fred)
(def f (future (some-sexp)))
(deref f)
请注意,在 Promise 中,您明确提供了您在以后的计算中选择的值(:fred
在这种情况下)。另一方面,未来正在被创造的地方消费。大概是在some-expr
幕后启动并串联计算(最终),但如果它在被访问时仍未评估,则线程阻塞直到它可用。
编辑添加
为了帮助进一步区分承诺和未来,请注意以下几点:
承诺
- 您创建一个
promise
. 该承诺对象现在可以传递给任何线程。 - 你继续计算。这些可能是非常复杂的计算,涉及副作用、下载数据、用户输入、数据库访问、其他承诺——无论你喜欢什么。该代码看起来非常像您在任何程序中的主线代码。
- 完成后,您可以
deliver
将结果传递给该 promise 对象。 deref
在您完成计算之前尝试兑现承诺的任何项目都将被阻止,直到您完成计算。一旦你完成并且你已经deliver
编辑了 promise,promise 就不会再阻塞了。
未来
- 你创造你的未来。你未来的一部分是计算的表达。
- 未来可能会或可能不会同时执行。它可以被分配一个线程,可能来自一个池。它只能等待,什么也不做。从您的角度来看,您无法分辨。
- 在某些时候,你(或另一个线程)
deref
是未来。如果计算已经完成,你会得到它的结果。如果它还没有完成,你会阻塞直到它完成。(大概如果它还没有开始,deref
ing 意味着它开始执行,但这也不能保证。)
虽然您可以使将来的表达式像创建 promise 之后的代码一样复杂,但是否可取是值得怀疑的。这意味着future 真的更适合快速、后台计算,而promise 更适合大型、复杂的执行路径。同样,就可用计算而言,promise 似乎更灵活一些,并且面向做工作的 promise 创建者和另一个收获收获的线程。Futures 更倾向于自动启动一个线程(没有丑陋和容易出错的开销)并继续处理其他事情,直到您 - 发起线程 - 需要结果。
Future 和 Promise 都是将异步计算结果从生产者传递给消费者的机制。
在Future的情况下,计算是在 Future 创建时定义的,异步执行“尽快”开始。它还“知道”如何产生异步计算。
在Promise计算的情况下,它的开始时间和 [可能的]异步调用与传递机制分离。当计算结果可用时,Producer 必须deliver
显式调用,这也意味着 Producer 控制结果何时可用。
对于Promises,Clojure 使用相同的对象(promise
调用结果)来生成(deliver
)和消耗(deref
)计算结果,这是一个设计错误。这是两个非常不同的功能,应该这样对待。
已经有很好的答案,所以只添加“如何使用”摘要:
两个都
创建 promise 或 future 会立即返回一个引用。这个引用在 @/deref 上阻塞,直到其他线程提供计算结果。
未来
创建未来时,您提供要完成的同步作业。它在专用无界池的线程中执行。
承诺
创建 Promise 时不提供任何参数。该引用应传递给其他将deliver
产生结果的“用户”线程。
在 Clojure 中promise
,future
、 和delay
是类似 Promise 的对象。它们都表示客户端可以通过使用deref
(或@
)等待的计算。客户端重用结果,因此计算不会运行多次。
它们的不同之处在于执行计算的方式:
future
将在不同的工作线程中开始计算。deref
将阻塞直到结果准备好。delay
当第一个客户端使用deref
, 或时,将懒惰地执行计算force
。promise
提供最大的灵活性,因为它的结果是通过使用deliver
. 当两者都不匹配future
或不delay
匹配您的用例时,您会使用它。
我认为Clojure for the Brave的第 9 章对delay
,future
和promise
.
统一这三个概念的想法是:任务生命周期。可以认为一个任务经历了三个阶段:定义任务、执行任务、使用任务结果。
一些编程语言(如 JavaScript)具有类似命名的结构(如 JS's Promise
),它们将任务生命周期中的几个(或所有)阶段耦合在一起。例如,在 JS 中,如果Promise
不为对象提供计算其值的函数(任务)或resolve
立即为它提供一个常量值,就不可能构造一个对象。
然而,Clojure 避免了这种耦合,因此它具有三个独立的构造,每个构造对应于任务生命周期中的一个阶段。
delay:
任务定义future
: 任务执行promise
: 任务结果
每个构造都只关注其自己的任务生命周期阶段,而不是其他任何东西,从而解开像 JS 这样的高阶构造Promise
并将它们分成适当的部分。
我们现在看到,在 JavaScript 中,aPromise
是上面列出的所有三个 Clojure 构造的组合。例子:
const promise = new Promise((resolve) => resolve(6))
让我们分解一下:
- 任务定义:
resolve(6)
就是任务。 - 任务执行:这里有一个隐含的执行上下文,即该任务将在事件循环的未来循环中运行。你在这方面没有发言权;例如,您不能要求同步解决此任务,因为异步性已融入
Promise
其自身。请注意在构建时如何安排Promise
您的任务运行(在某个未指定的时间)。你不能说“让我把它传递给我系统的不同组件,让它决定什么时候运行这个任务”。 - 任务结果:任务的结果被烘焙到
Promise
对象中,可以通过then
ing 或await
ing 获得。没有办法创建一个“空的”承诺结果,稍后由系统中一些未知的部分填写;您必须定义任务并同时安排它的执行。
PS:Clojure 强加的分离允许这些构造承担如果它们紧密耦合,它们将不适合的角色。例如,promise
从任务定义和执行中分离出来的 Clojure 现在可以用作线程之间的传输单元。
首先,aPromise
是Future
。我想你想知道 aPromise
和 a之间的区别FutureTask
。
AFuture
表示当前未知但将来会知道的值。
AFutureTask
表示将来会发生的计算结果(可能在某个线程池中)。当您尝试访问结果时,如果计算尚未发生,它会阻塞。否则立即返回结果。由于计算是您事先指定的,因此计算结果不涉及任何其他方。
APromise
表示未来将由承诺人交付给被承诺人的结果。在这种情况下,您是被许诺者,而许诺者是给您Promise
对象的人。与 类似FutureTask
,如果您尝试在Promise
完成之前访问结果,它将被阻止,直到承诺者完成Promise
. 一旦Promise
满足,您将始终立即获得相同的值。与 a 不同的是FutureTask
,这里涉及到另一方,它使Promise
. 另一方负责进行计算并完成Promise
.
从这个意义上说,aFutureTask
是Promise
你为自己创造的。