假设我需要运行两个并发计算,等待它们,然后组合它们的结果 . 更具体地说,我需要同时运行 f1: X1 => Y1
和 f2: X2 => Y2
,然后调用 f: (Y1, Y2) => Y
以最终获得 Y
的值 .
我可以创建未来的计算 fut1: X1 => Future[Y1]
和 fut2: X2 => Future[Y2]
,然后使用monadic组合将它们组合成 fut: (X1, X2) => Future[Y]
.
问题是monadic组合意味着顺序等待 . 在我们的例子中,它意味着我们先等待一个未来,然后我们将等待另一个未来 . 例如 . 如果它需要2秒 . 到第一个未来完成,只需1秒 . 到第二个未来失败,我们浪费1秒 .
因此,看起来我们需要期货的应用组合等待,直到完成或至少一个未来失败 . 是否有意义 ?你如何实现期货的 <*>
?
4 回答
其他答案中的所有方法都没有在未来快速失败的情况下做正确的事情,以及在很长一段时间后成功的未来 .
但是这种方法可以手动实现:
ScalaZ在内部做了类似的事情,因此在任何期货失败后,
f1 |@| f2
和_2639419都会立即失败 .以下是对这些方法失败时间的快速测试:
我的机器上的结果是:
不幸的是,这是真的 .
运行此代码输出:
我不确定应用程序组成与并发策略有什么关系 . 使用
for
comprehensions,如果所有期货完成或者如果其中任何一个失败,则会得到结果 . 所以它在语义上是一样的 .为什么它们按顺序运行
我认为期货顺序运行的原因是因为
step1
在step2
(以及其余计算中)可用 . 基本上我们可以将for
块转换为:因此,先前计算的结果可用于其余步骤 . 它在这方面与
<?>
和<*>
不同 .如何并行运行期货
@ andrey-tyukin的代码并行运行期货:
输出:
您的帖子似乎包含两个或多或少独立的问题 . 我将首先解决运行两个并发计算的具体实际问题 . 最后回答了关于
Applicative
的问题 .假设您有两个异步函数:
还有两个值:
现在,您可以通过多种不同方式开始计算 . 我们来看看其中的一些 .
Starting computations outside of for (parallel)
假设你这样做:
现在,计算
f1
和f2
已经在运行 . 您收集结果的顺序无关紧要 . 你可以用for
-理解来做到这一点:在
for
-comprehension中使用表达式y1
和y2
不会干扰y1
和y2
的计算顺序,它们仍然是并行计算的 .Starting computations inside of for (sequential)
如果我们只是简单地采用
y1
和y2
的定义,并将它们直接插入到for
理解中,我们仍会得到相同的结果,但执行顺序会有所不同:翻译成
特别是,第二次计算在第一次计算终止后开始 . 这通常不是人们想拥有的 .
这里违反了基本替代原则 . 如果没有副作用,可能会将此版本转换为前一版本,但在Scala中,必须明确地处理执行顺序 .
Zipping futures (parallel)
期货尊重产品 . 有一个方法
Future.zip
,它允许您这样做:这将并行运行两个计算,直到完成两个计算,或者直到其中一个计算失败 .
Demo
这是一个演示此行为的小脚本(受
muhuk
帖子的启发):输出:
Applicative
使用your other post中的此定义:
一个人可以这样做:
但是,我不确定这与你的具体问题有关,或者与之有关可理解和可读的代码 .
Future
已经是一个monad(这比Applicative
强),甚至还有内置的语法,所以我认为在这里添加一些Applicative
没有任何优势 .它不需要是顺序的 . 未来的计算可能会在创建未来的那一刻开始 . 当然,如果未来是由flatMap参数创建的(如果它需要第一次计算的结果则必须如此),那么它将是顺序的 . 但在诸如此类的代码中
你得到并发执行 .
所以Monad所暗示的Applicative的实现是可以的 .