我是并行执行和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
。