Scala未来序列和超时处理

Scala未来序列和超时处理,scala,timeout,future,Scala,Timeout,Future,有一些关于如何组合期货的好提示。 然而,我很好奇未来的序列如何做到这一点 我的第一种方法是这样的 import scala.concurrent._ import scala.concurrent.duration._ import scala.concurrent.ExecutionContext.Implicits._ object FutureSequenceScala extends App { println("Creating futureList") val timeo

有一些关于如何组合期货的好提示。 然而,我很好奇未来的序列如何做到这一点

我的第一种方法是这样的

import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._

object FutureSequenceScala extends App {
  println("Creating futureList")

  val timeout = 2 seconds
  val futures = List(1000, 1500, 1200, 800, 2000) map { ms =>
    val f = future {
      Thread sleep ms
      ms toString
    }
    Future firstCompletedOf Seq(f, fallback(timeout))
  }

  println("Creating waitinglist")
  val waitingList = Future sequence futures
  println("Created")

  val results = Await result (waitingList, timeout * futures.size)
  println(results)

  def fallback(timeout: Duration) = future {
    Thread sleep (timeout toMillis)
    "-1"
  }
}

是否有更好的方法来处理未来序列中的超时,或者这是一个有效的解决方案?

这里的代码中有一些内容可能需要重新考虑。对于初学者,我不太喜欢将任务提交到
ExecutionContext
中,这些任务的唯一目的是模拟超时,并且在其中使用了
Thread.sleep
sleep
调用正在阻塞,您可能希望避免执行上下文中的任务纯粹为了等待固定的时间而阻塞。我将从我的答案中剽窃,并建议对于纯超时处理,您应该使用类似于我在该答案中概述的内容。hashedweeltimer是一种高效的计时器实现,它比只休眠的任务更适合超时处理

现在,如果你走这条路线,我建议的下一个变化是处理每个未来与超时相关的故障。如果您希望单个故障使从
序列
调用返回的聚合
未来
完全失败,则不做任何额外的操作。如果您不希望发生这种情况,而是希望超时以返回一些默认值,那么您可以在
Future
上使用
recover
,如下所示:

withTimeout(someFuture).recover{
  case ex:TimeoutException => someDefaultValue
}
waitingList onComplete{
  case Success(results) => //handle success
  case Failure(ex) => //handle fail
}
完成此操作后,您可以利用非阻塞回调并执行如下操作:

withTimeout(someFuture).recover{
  case ex:TimeoutException => someDefaultValue
}
waitingList onComplete{
  case Success(results) => //handle success
  case Failure(ex) => //handle fail
}

每个未来都有一个超时,因此不会无限期地运行。IMO不需要在那里进行阻止,也不需要通过
atMost
param向
wait.result
提供额外的超时处理层。但我想这是假设您对非阻塞方法没有意见。如果您真的需要在那里阻塞,那么您不应该等待
timeout*futures.size
时间量。这些未来是并行运行的;那里的超时应该只需要与未来的单个超时一样长(或者稍微长一点,以考虑cpu/定时的延迟)。它当然不应该是超时*期货的总数

这里有一个版本显示了你的阻塞
回退
有多糟糕

请注意,执行器是单线程的,您正在创建许多回退

@cmbaxter是对的,您的主超时不应该是
timeout*futures.size
,它应该更大

@cmbaxter也正确地认为,您需要考虑非阻塞。一旦你这样做了,并且你想强制超时,那么你将为它选择一个计时器组件,查看他的链接答案(也链接到你的链接答案)

也就是说,我仍然喜欢坐在一个循环中等待下一件应该超时的事情,这真的很简单

它只需要一个期货列表、它们的超时时间和一个回退值

也许有这样一个用例,比如一个简单的应用程序,它只会阻塞一些结果(比如你的测试),并且在结果出现之前不能退出

import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext

import java.util.concurrent.Executors
import java.lang.System.{ nanoTime => now }

object Test extends App { 
  //implicit val xc = ExecutionContext.global
  implicit val xc = ExecutionContext fromExecutorService (Executors.newSingleThreadExecutor)

  def timed[A](body: =>A): A = {
    val start = now 
    val res = body
    val end = now
    Console println (Duration fromNanos end-start).toMillis + " " + res
    res
  }
  println("Creating futureList")

  val timeout = 1500 millis
  val futures = List(1000, 1500, 1200, 800, 2000) map { ms =>
    val f = future {
      timed {
        blocking(Thread sleep ms)
        ms toString
      }
    } 
    Future firstCompletedOf Seq(f, fallback(timeout))
  }   

  println("Creating waitinglist")
  val waitingList = Future sequence futures
  println("Created")

  timed {
  val results = Await result (waitingList, 2 * timeout * futures.size)
  println(results)
  }     
  xc.shutdown

  def fallback(timeout: Duration) = future {
    timed {
      blocking(Thread sleep (timeout toMillis))
      "-1"
    }
  }   
}   
发生了什么:

Creating futureList
Creating waitinglist
Created
1001 1000
1500 -1
1500 1500
1500 -1
1200 1200
1500 -1
800 800
1500 -1
2000 2000
1500 -1
List(1000, 1500, 1200, 800, 2000)
14007 ()
Monix任务具有以下支持:


好奇的是,
hashedweeltimer
如何比
TimerTask
newScheduledThreadPoolExecutor
更高效?两者都做相同的工作。@Jatin,我想您可以查看此链接以了解更多信息:。但从本质上讲,增加更多的任务不应该消耗更多的资源。它应该是一个更恒定的基于时间(就消耗的系统资源而言)的计时器,而不是类似于
计时器
计时器任务
的计时器。对于一个高吞吐量的系统,您将调度大量基于超时的短命任务,这是一个更好的解决方案,因为不断的资源使用声明。但是与
HashedWheelTimer
相比,使用
coresize
1
STPE
如何消耗更多的资源呢?对不起,我不明白<由于内部堆
O(log(n))
的原因,code>STPE的插入时间更长,但滴答时间更短。你能帮我吗explain@Jatin,当您可能以非常高的速率插入任务时,插入时间非常重要。根据你选择的滴答时间间隔,哈希轮计时器可能不那么精确,但如果这对你来说不像在计时器中输入和输出大量任务那么重要,那么它被认为是更好的选择。如果这意味着什么的话,Akka对他们的Actor
ask(?)
timeout处理使用同样的方法(包括HWT)。这就是我在
with timeout
函数中的代码基础。您也可以查看本文以了解更多信息: