scala Future如何允许异步执行



我是并行执行和scala的新手。关于在scala中使用Future,我有一些问题。

我相信Future允许异步并行执行。因此,根据我在以下代码中的理解,donutStock方法将在一个单独的线程上运行。官方文件还说,它并没有阻断主线。因此,如果主线程没有被阻塞,那么新的子线程和主线程应该并行执行。

因此,在下面的示例中,我希望一旦调用donutStock方法,主线程上的控件就应该继续,然后主线程应该调用另一个线程上的第二个donutStocks方法。

但是,我注意到第二个方法是在第一个调用完成之后才调用的。我对非阻塞或异步的理解是正确的吗?如果我想并行执行两个方法调用,那么正确的方法是什么。

我读到我们应该在服务器主线程中执行异步操作。在这种情况下异步操作的优点是什么

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
def donutStock(donut: String): Future[Int] =  Future {
(1 until 100).foreach { value ⇒
println(s"checking donut stock $donut")
}
10
}
donutStock("My Donut").onComplete{
case Success(value) ⇒ println("Call 1 Completed")
case Failure(exception) ⇒ println("Call 1 Failed")
}
donutStock("Your Donut").onComplete{
case Success(value) ⇒ println("Call 2 Completed")
case Failure(exception) ⇒ println("Call 2 Failed")
}

创建future时,通常使用一个线程立即启动。如果在您当前的执行上下文中没有可用的线程,那么它可能不会立即启动您的未来,而是等待线程释放。

如果您的执行上下文中只有一个线程可用,则可能会发生这种情况,下一个未来的执行将不得不等待上一个未来完成。

通常,执行上下文会有更多可用线程(例如,在scala的全局执行上下文中,线程数默认为可用线程数(。

在你的情况下,问题可能是,你第一个未来的结束可能太快了,以至于它在第二个开始之前就结束了。

您可以通过在打印值后引入小延迟来缓解这种情况,例如在println(s"checking donut stock $donut")之后添加Thread.sleep(10)

在这个变化之后,你未来的执行速度会变慢这可能会导致另一个问题,因为期货是在守护进程线程中启动的,所以可能会发生这种情况,即主线程将在期货执行结束之前终止。在这种情况下,它们将在调用onComplete回调之前终止。

为了避免这种情况,您可以使用Await等待两个期货,例如:

import scala.concurrent._
import scala.concurrent.duration._
val f1 = donutStock("My Donut").onComplete{
case Success(value) ⇒ println("Call 1 Completed")
case Failure(exception) ⇒ println("Call 1 Failed")
}
val f2 = donutStock("Your Donut").onComplete{
case Success(value) ⇒ println("Call 2 Completed")
case Failure(exception) ⇒ println("Call 2 Failed")
}
val result1 = Await.result(f1, 1 second)
val result2 = Await.result(f2, 1 second)

如果我们可以等待未来,那么onComplete回调的用例是什么?例如,当我们定义了一个返回Future的函数,并且我们不想使用Await阻止它,但我们仍然希望在Future完成时执行一些操作时,这可能会有所帮助。

例如,您可以将donutStock修改如下:

def donutStock(donut: String, idx: Int): Future[Int] = {
val f = Future {
(1 until 100).foreach { value ⇒
println(s"checking donut stock $donut")
}
10
}
//we don't block future, but onComplete callback will be still executed when future ends
f.onComplete{
case Success(value) ⇒ println(s"Call $idx Completed")
case Failure(exception) ⇒ println(s"Call $idx Failed")
}
f 
}

Futures是在Scala中编写多线程代码的标准机制。每当我们创建一个新的Future操作时,Scala都会生成一个新线程来运行该Future的代码,并在完成后执行任何提供的回调。

为了使用Futures,Scala要求我们提供一个隐式执行上下文,它控制Futures执行的线程池。我们可以创建自己的执行上下文,或者使用默认的上下文,这通常就足够了。默认执行上下文由Fork Join线程池支持。从代码中可以明显看出,该示例使用了隐式代码。

def donutStock(donut: String): Future[Int] =  Future {
(1 until 100).foreach { value ⇒
println(s"checking donut stock $donut")
}
10
}

当函数donutStock(<string>)返回类型为Future[Int]时,上面所附的代码将在自己的线程中执行。

Scala允许我们定义回调函数,这些函数在Future成功或失败时执行。与此同时,创建Future的线程被解除阻塞,并可能继续执行,如下所示,

donutStock("My Donut").onComplete{
case Success(value) ⇒ println("Call 1 Completed")
case Failure(exception) ⇒ println("Call 1 Failed")
}
donutStock("Your Donut").onComplete{
case Success(value) ⇒ println("Call 2 Completed")
case Failure(exception) ⇒ println("Call 2 Failed")
}

Future donutStock((成功完成后,onComplete回调将接收一个包含结果的Success对象作为10

最新更新