it-swarm.com.de

Scala-Futures - Zeitüberschreitung?

es gibt einen Aspekt von Futures, den ich aus dem offiziellen Tutorial Ref nicht genau verstehe. http://docs.scala-lang.org/overviews/core/futures.html

Haben Futures in Scala einen eingebauten Time-Out-Mechanismus? Nehmen wir an, das Beispiel unten wäre eine 5-Gigabyte-Textdatei ... bewirkt der implizierte Geltungsbereich von "Implicits.global" schließlich, dass onFailure auf nicht blockierende Weise ausgelöst wird oder kann dies definiert werden? Und ohne eine Standard-Auszeit würde dies nicht bedeuten, dass weder Erfolg noch Misserfolg jemals ausgelöst werden könnten? 

import scala.concurrent._
import ExecutionContext.Implicits.global

val firstOccurence: Future[Int] = future {
  val source = scala.io.Source.fromFile("myText.txt")
  source.toSeq.indexOfSlice("myKeyword")
}
firstOccurence onSuccess {
  case idx => println("The keyword first appears at position: " + idx)
}
firstOccurence onFailure {
  case t => println("Could not process file: " + t.getMessage)
}
49
LaloInDublin

Sie erhalten nur dann ein Timeout-Verhalten, wenn Sie blockieren, um die Ergebnisse der Future abzurufen. Wenn Sie die nicht blockierenden Rückrufe onComplete, onSuccess oder onFailure verwenden möchten, müssen Sie Ihre eigene Timeout-Behandlung durchführen. Akka verfügt über eine integrierte Zeitüberschreitungsverarbeitung für die Beantwortung von Anfragen/Antworten (?) zwischen den Schauspielern. FWIW, in Akka, setzen sie für die Timeout-Behandlung zwei Futures über Future.firstCompletedOf zusammen, eine, die die eigentliche async-Task darstellt, und eine, die das Timeout darstellt. Wenn der Timeout-Timer (über eine Variable HashedWheelTimer) zuerst erscheint, erhalten Sie einen Fehler beim asynchronen Rückruf.

Ein sehr vereinfachtes Beispiel für das Rollen Ihrer eigenen Elemente könnte in etwa so aussehen. Zunächst ein Objekt zum Planen von Timeouts:

import org.jboss.netty.util.{HashedWheelTimer, TimerTask, Timeout}
import Java.util.concurrent.TimeUnit
import scala.concurrent.duration.Duration
import scala.concurrent.Promise
import Java.util.concurrent.TimeoutException

object TimeoutScheduler{
  val timer = new HashedWheelTimer(10, TimeUnit.MILLISECONDS)
  def scheduleTimeout(promise:Promise[_], after:Duration) = {
    timer.newTimeout(new TimerTask{
      def run(timeout:Timeout){              
        promise.failure(new TimeoutException("Operation timed out after " + after.toMillis + " millis"))        
      }
    }, after.toNanos, TimeUnit.NANOSECONDS)
  }
}

Dann eine Funktion, um eine Zukunft zu nehmen und ihr ein Timeout-Verhalten hinzuzufügen:

import scala.concurrent.{Future, ExecutionContext, Promise}
import scala.concurrent.duration.Duration

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
}

Beachten Sie, dass die HashedWheelTimer, die ich hier verwende, von Netty stammt.

64
cmbaxter

Ich habe gerade eine TimeoutFuture-Klasse für einen Kollegen erstellt:

TimeoutFuture

package model

import scala.concurrent._
import scala.concurrent.duration._
import play.libs.Akka
import play.api.libs.concurrent.Execution.Implicits._

object TimeoutFuture {
  def apply[A](timeout: FiniteDuration)(block: => A): Future[A] = {

    val prom = promise[A]

    // timeout logic
    Akka.system.scheduler.scheduleOnce(timeout) {
      prom tryFailure new Java.util.concurrent.TimeoutException
    }

    // business logic
    Future { 
      prom success block
    }

    prom.future
  } 
}

Verwendungszweck

val future = TimeoutFuture(10 seconds) { 
  // do stuff here
}

future onComplete {
  case Success(stuff) => // use "stuff"
  case Failure(exception) => // catch exception (either TimeoutException or an exception inside the given block)
}

Anmerkungen:

  • Nimmt an, Spiel! Rahmen (aber es ist leicht genug, sich anzupassen)
  • Jeder Code läuft in demselben ExecutionContext, was möglicherweise nicht ideal ist.
21
Pablo Fernandez

Alle diese Antworten erfordern zusätzliche Abhängigkeiten. Ich entschied mich dazu, eine Version mit Java.util.Timer zu schreiben, die eine effiziente Möglichkeit darstellt, eine Funktion in der Zukunft auszuführen, in diesem Fall ein Timeout auszulösen. 

Blogpost mit mehr Details hier

Mit diesem Versprechen von Scala können wir eine Zukunft mit Timeout wie folgt gestalten: 

package justinhj.concurrency

import Java.util.concurrent.TimeoutException
import Java.util.{Timer, TimerTask}

import scala.concurrent.duration.FiniteDuration
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.language.postfixOps

object FutureUtil {

  // All Future's that use futureWithTimeout will use the same Timer object
  // it is thread safe and scales to thousands of active timers
  // The true parameter ensures that timeout timers are daemon threads and do not stop
  // the program from shutting down

  val timer: Timer = new Timer(true)

  /**
    * Returns the result of the provided future within the given time or a timeout exception, whichever is first
    * This uses Java Timer which runs a single thread to handle all futureWithTimeouts and does not block like a
    * Thread.sleep would
    * @param future Caller passes a future to execute
    * @param timeout Time before we return a Timeout exception instead of future's outcome
    * @return Future[T]
    */
  def futureWithTimeout[T](future : Future[T], timeout : FiniteDuration)(implicit ec: ExecutionContext): Future[T] = {

    // Promise will be fulfilled with either the callers Future or the timer task if it times out
    val p = Promise[T]

    // and a Timer task to handle timing out

    val timerTask = new TimerTask() {
      def run() : Unit = {
            p.tryFailure(new TimeoutException())
        }
      }

    // Set the timeout to check in the future
    timer.schedule(timerTask, timeout.toMillis)

    future.map {
      a =>
        if(p.trySuccess(a)) {
          timerTask.cancel()
        }
    }
    .recover {
      case e: Exception =>
        if(p.tryFailure(e)) {
          timerTask.cancel()
        }
    }

    p.future
  }

}
15
justinhj

Das Play Framework enthält Promise.timeout, damit Sie Code wie folgt schreiben können

private def get(): Future[Option[Boolean]] = {
  val timeoutFuture = Promise.timeout(None, Duration("1s"))
  val mayBeHaveData = Future{
    // do something
    Some(true)
  }

  // if timeout occurred then None will be result of method
  Future.firstCompletedOf(List(mayBeHaveData, timeoutFuture))
}
5
Kir

Wenn Sie möchten, dass der Verfasser (Versprechungsinhaber) die Zeitüberschreitungslogik steuert, verwenden Sie akka.pattern.after auf folgende Weise:

val timeout = akka.pattern.after(10 seconds, system.scheduler)(Future.failed(new TimeoutException(s"timed out during...")))
Future.firstCompletedOf(Seq(promiseRef.future, timeout))

Auf diese Weise wird die Zukunft Ihres Anrufers zu einem bestimmten Zeitpunkt mit einem Fehler abgeschlossen, wenn Ihre Versprechen-Abschlusslogik niemals stattfindet.

3
galbarm

Ich bin ziemlich überrascht, dass dies in Scala kein Standard ist. Meine Versionen sind kurz und haben keine Abhängigkeiten

import scala.concurrent.Future

sealed class TimeoutException extends RuntimeException

object FutureTimeout {

  import scala.concurrent.ExecutionContext.Implicits.global

  implicit class FutureTimeoutLike[T](f: Future[T]) {
    def withTimeout(ms: Long): Future[T] = Future.firstCompletedOf(List(f, Future {
      Thread.sleep(ms)
      throw new TimeoutException
    }))

    lazy val withTimeout: Future[T] = withTimeout(2000) // default 2s timeout
  }

}

Verwendungsbeispiel

import FutureTimeout._
Future { /* do smth */ } withTimeout
3
Raul

Niemand hat akka-streams bisher erwähnt. Die Flows haben eine einfache completionTimeout -Methode, und die Anwendung auf einen Single-Source-Stream funktioniert wie eine Zukunft.

Akka-Streams können jedoch auch abgebrochen werden, sodass die Quelle tatsächlich nicht mehr ausgeführt werden kann, d. H.

3
akauppi

Sie können das Zeitlimit angeben, wenn Sie auf die Zukunft warten:

Für scala.concurrent.Future können Sie mit der result-Methode ein Timeout angeben.

Bei scala.actors.Future können Sie mit Futures.awaitAll ein Timeout angeben.

Ich glaube nicht, dass in die Ausführung einer Zukunft ein Timeout eingebaut ist.

3
gzm0

Monix Task hat Zeitüberschreitung Unterstützung

import monix.execution.Scheduler.Implicits.global
import monix.eval._
import scala.concurrent.duration._
import scala.concurrent.TimeoutException

val source = Task("Hello!").delayExecution(10.seconds)

// Triggers error if the source does not complete in 3 seconds after runOnComplete
val timedOut = source.timeout(3.seconds)

timedOut.runOnComplete(r => println(r))
//=> Failure(TimeoutException)
1
WeiChing Lin

Diese Version funktioniert ohne Timeout

import scala.concurrent._
import scala.concurrent.duration.FiniteDuration

object TimeoutFuture {
    def apply[A](
        timeout: FiniteDuration
    )(block: => A)(implicit executor: ExecutionContext): Future[A] =
        try {
            Future { Await.result(Future { block }, timeout) }
        } catch {
            case _: TimeoutException => Future.failed(new TimeoutException(s"Timed out after ${timeout.toString}"))
        }
}
0
unveloper

Die einfachste Möglichkeit, ein Zeitlimit für Future IMO festzulegen, ist der in Scala integrierte Mechanismus, der scala.concurrent.Await.ready verwendet. Dies löst eine TimeoutException aus, wenn der Future länger als das angegebene Zeitlimit dauert. Andernfalls wird die Zukunft selbst zurückgegeben. Hier ist ein einfaches Beispiel

import scala.concurrent.ExecutionContext.Implicits._
import scala.concurrent.duration._
val f1: Future[Int] = Future {
  Thread.sleep(1100)
  5
}

val fDoesntTimeout: Future[Int] = Await.ready(f1, 2000 milliseconds)

val f: Future[Int] = Future {
  Thread.sleep(1100)
  5
}
val fTimesOut: Future[Int] = Await.ready(f, 100 milliseconds)
0
sparker

Ich verwende diese Version (basierend auf dem obigen Play-Beispiel), die den System-Dispatcher von Akka verwendet:

object TimeoutFuture {
  def apply[A](system: ActorSystem, timeout: FiniteDuration)(block: => A): Future[A] = {
    implicit val executionContext = system.dispatcher

    val prom = Promise[A]

    // timeout logic
    system.scheduler.scheduleOnce(timeout) {
      prom tryFailure new Java.util.concurrent.TimeoutException
    }

    // business logic
    Future {
      try {
        prom success block
      } catch {
        case t: Throwable => prom tryFailure t
      }
    }

    prom.future
  }
}
0
PJ Fanning