简体   繁体   English

具有多个依赖关系的Scala期货

[英]Scala Futures with multiple dependencies

I have to compute asynchronously a set of features that can have multiple dependencies between each other (no loops). 我必须异步计算一组功能,这些功能之间可以有多个依赖关系(无循环)。 For example 例如

 class FeatureEncoderMock(val n:String, val deps: List[String] = List.empty) {
      def compute = {
          println(s"starting computation feature $n")
          Thread.sleep(r.nextInt(2500))
          println(s"end computation feature $n")
      }
  }

  val registry = Map(
        "feat1" -> new FeatureEncoderMock("feat1", List("factLogA", "factLogB")),
        "factLogA" -> new FeatureEncoderMock("factLogA"),
        "factLogB" -> new FeatureEncoderMock("factLogB"),
        "feat1" -> new FeatureEncoderMock("feat1", List("factLogA", "factLogB")),
        "feat2" -> new FeatureEncoderMock("feat2", List("factLogA")),
        "feat3" -> new FeatureEncoderMock("feat3", List("feat1")),
        "feat4" -> new FeatureEncoderMock("feat4", List("feat3", "factLogB"))
  )

What I want to achieve is call a single function on feat4 that will trigger the computation of all dependent features and will take care of dependencies among them. 我要实现的是在feat4上调用单个函数,该函数将触发所有依赖功能的计算,并将处理它们之间的依赖关系。 I tried with this 我尝试了这个

def run(): Unit = {
val requested = "feat4"

val allFeatures = getChainOfDependencies(requested)

val promises = allFeatures.zip(Seq.fill(allFeatures.size)(Promise[Unit])).toMap

def computeWithDependencies(f: String) = Future {
  println(s"computing $f")
  val encoder = registry(f)

  if(encoder.deps.isEmpty) {
    promises(f).success(registry(f).compute)
  }
  else {
    val depTasks = promises.filterKeys(encoder.deps.contains)

    val depTasksFuture = Future.sequence(depTasks.map(_._2.future))

    depTasksFuture.onSuccess({
      case _ =>
        println(s"all deps for $f has been computed")
        promises(f).success(registry(f).compute)
        println(s"done for $f")
    })
  }
 }

computeWithDependencies(requested)
}

But I cannot understand why the order of execution is not as expected. 但是我不明白为什么执行顺序不符合预期。 I am not sure what is the proper way to feed the future inside a promise. 我不确定在诺言中养育未来的正确方法是什么。 I am quite sure that this piece of code is wrong on that part. 我很确定这段代码在那部分上是错误的。

I think you're overthinking it with the promises; 我认为您对诺言的想法过高; Future composition is probably all that you need. Future组成可能就是您所需要的。 Something like this: 像这样:

import scala.collection.mutable

def computeWithDependencies(s: String, cache: mutable.Map[String, Future[Unit]] = mutable.Map.empty)
                           (implicit ec: ExecutionContext): Future[Unit] = {
  cache.get(s) match {
    case Some(f) => f
    case None => {
      val encoder = registry(s)
      val depsFutures = encoder.deps.map(d => computeWithDependencies(d, cache))
      val result = Future.sequence(depsFutures).flatMap(_ => Future { encoder.compute })
      cache += s -> result
      result
    }
  }
}

The call to flatMap ensures that all of the dependency futures complete before the "current" future executes, even if the result (a List[Unit] ) is ignored. flatMap的调用可确保所有依赖项flatMap在“当前” Future执行之前完成,即使结果( List[Unit] )被忽略。 The business with the cache is just to prevent recomputation if the dependency graph has a "diamond" in it, but could be left out if it won't or if you're ok with recomputing. 如果依赖关系图中包含“菱形”,则具有缓存的业务只是为了防止重新计算,但是如果不这样做或您可以进行重新计算,则可以将其忽略。 Anyway, when running this: 无论如何,运行此命令时:

val futureResult = computeWithDependencies("feat4")
Await.result(futureResult, 30 seconds)

I see this output: 我看到以下输出:

starting computation feature factLogB
starting computation feature factLogA
end computation feature factLogB
end computation feature factLogA
starting computation feature feat1
end computation feature feat1
starting computation feature feat3
end computation feature feat3
starting computation feature feat4
end computation feature feat4

Which seems correct to me. 这对我来说似乎是正确的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM