Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading scala Future如何允许异步执行?_Multithreading_Scala_Asynchronous_Future - Fatal编程技术网

Multithreading scala Future如何允许异步执行?

Multithreading scala Future如何允许异步执行?,multithreading,scala,asynchronous,future,Multithreading,Scala,Asynchronous,Future,我不熟悉并行执行和scala。 关于在scala中使用Future,我有几个问题 我相信未来会允许异步并行执行。因此,根据我在下面代码中的理解,donutStock方法将在单独的线程上运行。官方文件还说,它并没有阻挡主线。因此,如果主线程没有被阻塞,那么新的子线程和主线程应该并行执行 因此,在下面的示例中,我希望只要调用了donutStock方法,主线程上的控件就应该前进,然后主线程应该调用另一个线程上的第二个donutStock方法 但是,我注意到第二个方法只有在第一次调用完成后才被调用。我对

我不熟悉并行执行和scala。 关于在scala中使用Future,我有几个问题

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

因此,在下面的示例中,我希望只要调用了donutStock方法,主线程上的控件就应该前进,然后主线程应该调用另一个线程上的第二个donutStock方法

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

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

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")
}

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

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

当函数
donutStock()
返回类型为
Future[Int]
时,上述附带的代码将在其自己的线程中执行

Scala允许我们定义回调函数,这些函数在未来的成功或失败时执行。同时,创建未来的线程被解锁,并可能继续执行如下操作:

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对象,其中包含as
10

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

如果在您的执行上下文中只有一个线程可用,那么它可能会发生,下一个将来的执行将不得不等待上一个将来完成

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

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

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

在这个改变之后,你的未来将会执行得更慢 这可能会导致另一个问题,即由于未来在守护进程线程中启动,因此可能会发生这种情况,即主线程将在未来的执行结束之前终止。在这种情况下,它们将在调用
onComplete
回调之前终止

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

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 
}

你的理解是正确的。但是您的执行上下文可能只有一个线程?您能告诉我如何检查执行上下文中可用的线程数吗?您能在
println(s“checking donut stock$donut”)
之后添加
thread.sleep(10)
,然后再次检查吗?我认为你的第一个未来在第二个未来开始之前就结束了。@KrzysztofAtłasik感谢你的回答。我试过你的建议。我可以看到来自这两种方法的消息交织在一起。但是,它们都没有打印全部100行。@AbhishekGupta现在是另一个问题:futures的线程作为守护进程启动,这意味着,当主线程完成时,它们也将终止。您必须让主线程等待期货正常结束。最简单的解决方案是在主函数的末尾添加类似于
Thread.sleep(2000)
的内容,这将使它延迟足够长的时间,使您的未来调用
onComplete
回调。如果您对更复杂的解决方案感兴趣,请查看。
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 
}