在Scala时间有限的尽力而为计算中获得部分结果

时间:2018-08-10 16:17:04

标签: scala

尝试在给定的时间范围内执行功能,但是如果超时导致计算失败,则会得到部分结果,而不是空异常。

随附的代码解决了该问题。

timedRun函数来自Computation with time limit

有更好的方法吗?。

package ga                                                                                                                                                                                                    


object Ga extends App {                                                                                                                                                                                       

  //this is the ugly...                                                                                                                                                                                       
  var bestResult = "best result";                                                                                                                                                                             
  try {                                                                                                                                                                                                       
    val result = timedRun(150)(bestEffort())                                                                                                                                                                  
  } catch {                                                                                                                                                                                                   
    case e: Exception =>                                                                                                                                                                                      
      print ("timed at = ")                                                                                                                                                                                   
  }                                                                                                                                                                                                           
  println(bestResult)                                                                                                                                                                                         

  //dummy function                                                                                                                                                                                            
  def bestEffort(): String = {                                                                                                                                                                                
    var res = 0                                                                                                                                                                                               
    for (i <- 0 until 100000) {                                                                                                                                                                               
      res = i                                                                                                                                                                                                 
      bestResult = s" $res"                                                                                                                                                                                   
    }                                                                                                                                                                                                         
    " " + res                                                                                                                                                                                                 
  }                                                                                                                                                                                                           

  //This is the elegant part from stackoverflow  gruenewa                                                                                                                                                     
  @throws(classOf[java.util.concurrent.TimeoutException])                                                                                                                                                     
  def timedRun[F](timeout: Long)(f: => F): F = {                                                                                                                                                              
    import java.util.concurrent.{ Callable, FutureTask, TimeUnit }                                                                                                                                            
    val task = new FutureTask(new Callable[F]() {                                                                                                                                                             
      def call() = f                                                                                                                                                                                          
    })                                                                                                                                                                                                        
    new Thread(task).start()                                                                                                                                                                                  
    task.get(timeout, TimeUnit.MILLISECONDS)                                                                                                                                                                  
  }                                                                                                                                                                                                           
}                                                                                                                                                                                                             

2 个答案:

答案 0 :(得分:1)

我将引入一个小的中间类,以更明确地在线程之间传递部分结果。这样,您不必以任何令人惊讶的方式修改非本地状态。然后,您也可以在args.caller(element)方法中捕获异常:

timedRun

由于您通常没有传递作为参数的函数的“返回值”,因此这有点尴尬。但是我认为这是对代码进行的最根本的修改才有意义。您还可以考虑将计算建模为返回部分结果的 class Result[A](var result: A) val result = timedRun(150)("best result")(bestEffort) println(result) //dummy function def bestEffort(r: Result[String]): Unit = { var res = 0 for (i <- 0 until 100000) { res = i r.result = s" $res" } r.result = " " + res } def timedRun[A](timeout: Long)(initial: A)(f: Result[A] => _): A = { import java.util.concurrent.{ Callable, FutureTask, TimeUnit } val result = new Result(initial) val task = new FutureTask(new Callable[A]() { def call() = { f(result); result.result } }) new Thread(task).start() try { task.get(timeout, TimeUnit.MILLISECONDS) } catch { case e: java.util.concurrent.TimeoutException => result.result } } Stream,然后本质上执行Iterator的模型。但是,实际可行程度取决于实际计算。

答案 1 :(得分:1)

首先,您需要使用一种解决方案来在将来超时后恢复,但不幸的是Scala中没有内置这些​​解决方案: 参见:Scala Futures - built in timeout? 例如:

   def withTimeout[T](fut:Future[T])(implicit ec:ExecutionContext, after:Duration) = {
      val prom = Promise[T]()
      val timeout = TimeoutScheduler.scheduleTimeout(prom, after)
      val combinedFut = Future.firstCompletedOf(List(fut, prom.future))
      fut onComplete{case result => timeout.cancel()}
      combinedFut
    }

那么简单:

var bestResult = "best result"

val expensiveFunction = Future {
  var res = 0
  for (i <- 0 until 10000) {
    Thread.sleep(10)
    res = i
    bestResult = s" $res"
  }
  " " + res
}

val timeoutFuture = withTimeout(expensiveFunction) recover {
  case _: TimeoutException => bestResult
}

println(Await.result(timeoutFuture, 1 seconds))