我最近开始从事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..")
})
有人可以解决我的疑问吗?
答案 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
并不意味着要管理共享状态,但是它们对于编写异步代码非常有用。您可以使用map
,flatMap
和许多其他操作来组合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
,可以依次使用Future
,map
或其他方法对其进行处理。