为什么不建议从Scala的Future中获取价值?

时间:2019-04-10 06:30:41

标签: scala

我最近开始从事Scala的开发,并遇到了名为Future的功能。我已经发布了一个有关代码及其帮助的问题。

在那次谈话中,我被告知不建议从Future检索值。

我知道执行时它是一个并行过程,但是如果不建议检索Future的值,我如何/何时访问它的结果?如果Future的目的是独立于主线程运行线程/进程,为什么不建议您访问它呢?未来会自动将其输出分配给呼叫者吗?如果是这样,我们怎么知道何时访问它?

我编写了以下代码,以返回带有Map[String, String]的Future。

def getBounds(incLogIdMap:scala.collection.mutable.Map[String, String]): Future[scala.collection.mutable.Map[String, String]] = Future {
  var boundsMap = scala.collection.mutable.Map[String, String]()
  incLogIdMap.keys.foreach(table => if(!incLogIdMap(table).contains("INVALID")) {
    val minMax    = s"select max(cast(to_char(update_tms,'yyyyddmmhhmmss') as bigint)) maxTms, min(cast(to_char(update_tms,'yyyyddmmhhmmss') as bigint)) minTms from queue.${table} where key_ids in (${incLogIdMap(table)})"
    val boundsDF  = spark.read.format("jdbc").option("url", commonParams.getGpConUrl()).option("dbtable", s"(${minMax}) as ctids")
      .option("user", commonParams.getGpUserName()).option("password", commonParams.getGpPwd()).load()
    val maxTms = boundsDF.select("minTms").head.getLong(0).toString + "," + boundsDF.select("maxTms").head.getLong(0).toString
    boundsMap += (table -> maxTms)
  }
  )
  boundsMap
}

如果我必须使用getBounds方法返回的值,我可以通过以下方式访问它吗?

val tmsobj = new MinMaxVals(spark, commonParams)
  tmsobj.getBounds(incLogIds) onComplete ({ 
  case Success(Map) => val boundsMap = tmsobj.getBounds(incLogIds) 
  case Failure(value) => println("Future failed..") 
})

有人可以解决我的疑问吗?

3 个答案:

答案 0 :(得分:2)

正如其他人所指出的那样,等待Future中获取值首先要打败启动Future的整个过程。

但是onComplete()不会导致其余代码等待,它只是附加了一些额外的指令,这些指令将作为Future线程的一部分而执行,而其余代码将继续进行方式。

那么您提议的访问getBounds()结果的代码有什么问题?让我们来看一下。

tmsobj.getBounds(incLogIds) onComplete { //launch Future, when it completes ...
  case Success(m) => //if Success then store the result Map in local variable "m"
    val boundsMap = tmsobj.getBounds(incLogIds) //launch a new and different Future
    //boundsMap is a local variable, it disappears after this code block

  case Failure(value) => //if Failure then store error in local variable "value"
    println("Future failed..") //send some info to STDOUT 
}//end of code block

您会注意到,我将Success(Map)更改为Success(m)是因为Map是一种类型(它是一个伴随对象),不能用来匹配您的{{ 1}}。

结论:Future不会使您的代码在onComplete()上等待,这很好,但是由于它返回Future,即它没有返回,因此它在某种程度上受到限制。可以用来传达Unit结果的值。

答案 1 :(得分:1)

TLDR; Futures并不意味着要管理共享状态,但是它们对于编写异步代码非常有用。您可以使用mapflatMap和许多其他操作来组合Futures

Future表示的计算将使用给定的ExecutionContext(通常是隐式给出的)执行,它通常位于线程池中,因此您可以假设{{ 1}}计算是并行进行的。由于这种并发性,通常不建议对从Future主体内部共享的状态进行突变,例如:

Future

因为这样您就有冒着在另一个线程(也可能是“主”线程)中访问/修改 var i: Int = 0 val f: Future[Unit] = Future { // Some computation i = 42 } 的风险。在这种并发访问情况下,i可能不是正确的并发模型,您可以想象使用监视器或消息传递。

另一个诱人但又不鼓励的可能性是阻塞主线程,直到结果可用:

Futures

这很不好的原因是,您将完全阻塞主线程,从而使首先执行并发执行的好处消失了。如果执行过多此操作,则可能还会由于大量线程被阻塞并占用资源而遇到麻烦。

您如何知道何时访问结果?事实并非如此,这实际上就是您应该尝试组成val f: Future[Init] = Future { 42 } val i: Int = Await.result(f) 的原因,并且仅在应用程序的最边缘订阅它们的Futures方法。大多数方法通常采用并返回onComplete,并且仅在非常特定的位置订阅它们。

答案 2 :(得分:0)

不建议使用Future 等待,因为这会阻塞当前线程的执行,直到将来某个未知时刻为止,这可能会永远持续下去。 / p>

通过将处理函数传递给Await.result上的Future之类的调用来处理map的值是完全可以的。将来完成时,这将调用您的函数。 Future的结果是另一个map,可以依次使用Futuremap或其他方法对其进行处理。