Scala Future / Promise快速失败管道

时间:2016-09-11 15:06:09

标签: multithreading scala promise

我希望并行启动两个或更多Future / Promises,即使其中一个已启动的Future / Promise失败并且不想等待其余的完成,也会失败。 在Scala中组合此管道的最惯用方法是什么。

编辑:更多上下文信息。

我必须启动两个外部进程,一个写入fifo文件,另一个从中读取。假设作者进程失败;读者线程可能永远挂起等待文件的任何输入。所以我想要并行启动这两个进程,并且快速失败,即使其中一个Future / Promise失败而没有等待另一个进程完成。

以下是更精确的示例代码。命令不完全是cattail。为了简洁,我使用了它们。

val future1 = Future { executeShellCommand("cat file.txt > fifo.pipe") }
val future2 = Future { executeShellCommand("tail fifo.pipe") }

3 个答案:

答案 0 :(得分:6)

如果我正确理解了这个问题,我们正在寻找的是一个快速失败的序列实现,它类似于firstCompletedOf

的失败偏向版本

在这里,我们急切地注册了一个失败的回调,以防一个期货早期失败,确保我们一旦期货失败就会失败。

import scala.concurrent.{Future, Promise}
import scala.util.{Success, Failure}
import scala.concurrent.ExecutionContext.Implicits.global
def failFast[T](futures: Seq[Future[T]]): Future[Seq[T]] = {
  val promise = Promise[Seq[T]]
  futures.foreach{f => f.onFailure{case ex => promise.failure(ex)}}
  val res = Future.sequence(futures)
  promise.completeWith(res).future
}

Future.sequence相反,只要期货的任何失败,此实施将失败,无论订购如何。 让我们用一个例子来说明:

import scala.util.Try
// help method to measure time
def resilientTime[T](t: =>T):(Try[T], Long) = {
  val t0 = System.currentTimeMillis
  val res = Try(t)
  (res, System.currentTimeMillis-t0)
}

import scala.concurrent.duration._
import scala.concurrent.Await

第一个未来将失败(2秒内失败)

val f1 = Future[Int]{Thread.sleep(2000); throw new Exception("boom")}
val f2 = Future[Int]{Thread.sleep(5000); 42}
val f3 = Future[Int]{Thread.sleep(10000); 101}
val res = failFast(Seq(f1,f2,f3))

resilientTime(Await.result(res, 10.seconds))
// res: (scala.util.Try[Seq[Int]], Long) = (Failure(java.lang.Exception: boom),1998)

最后的未来将会失败。失败也在2秒内。 (注意序列构造中的顺序)

val f1 = Future[Int]{Thread.sleep(2000); throw new Exception("boom")}
val f2 = Future[Int]{Thread.sleep(5000); 42}
val f3 = Future[Int]{Thread.sleep(10000); 101}
val res = failFast(Seq(f3,f2,f1))

resilientTime(Await.result(res, 10.seconds))
// res: (scala.util.Try[Seq[Int]], Long) = (Failure(java.lang.Exception: boom),1998)

Future.sequence相比,失败取决于排序(10秒内失败):

val f1 = Future[Int]{Thread.sleep(2000); throw new Exception("boom")}
val f2 = Future[Int]{Thread.sleep(5000); 42}
val f3 = Future[Int]{Thread.sleep(10000); 101}
val seq = Seq(f3,f2,f1)

resilientTime(Await.result(Future.sequence(seq), 10.seconds))
//res: (scala.util.Try[Seq[Int]], Long) = (Failure(java.lang.Exception: boom),10000)

答案 1 :(得分:1)

使用Future.sequence

val both = Future.sequence(Seq(
  firstFuture,
  secondFuture));

这是汇总两个或更多期货的正确方法,其中一个期货的失败未通过汇总的未来,而当所有内部期货完成时,汇总的未来完成。这个答案的旧版本提出了一个理解,虽然非常普遍,但不会立即拒绝其中一个期货拒绝,而是等待它。

答案 2 :(得分:0)

压缩期货

class MyFormType extends AbstractType
{
    ...

    /**
     * This will remove formTypeName from the form
     * @return null
     */
    public function getBlockPrefix() {
        return null;
    }
}
如果val f1 = Future { doSomething() } val f2 = Future { doSomething() } val resultF = f1 zip f2 resultF中的任何一个失败,

f1将来会失败

解决的时间是f2

min(f1time, f2time)