web-dev-qa-db-ja.com

Scala Futures-built in timeout?

公式チュートリアルrefから正確に理解できない先物の側面があります。 http://docs.scala-lang.org/overviews/core/futures.html

scalaのfuturesには何らかの種類のタイムアウト機構が組み込まれていますか?下の例は5ギガバイトのテキストファイルでした...最終的に「Implicits.global」の暗黙のスコープを行いますonFailureをブロックしない方法で起動するか、それを定義できますか?そして、何らかのデフォルトのタイムアウトがなければ、成功も失敗も起動しない可能性があることを意味しませんか?

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)
}
52
LaloInDublin

Futureの結果を取得するためにブロッキングを使用する場合にのみ、タイムアウト動作が発生します。ノンブロッキングコールバックonCompleteonSuccess、またはonFailureを使用する場合は、独自のタイムアウト処理をロールする必要があります。 Akkaには、アクター間の要求/応答(?)メッセージングのタイムアウト処理が組み込まれていますが、Akkaの使用を開始するかどうかは不明です。 AWIのFWIWは、タイムアウト処理のために、Future.firstCompletedOfを介して2つのFuturesを構成します。1つは実際の非同期タスクを表し、もう1つはタイムアウトを表します。 (HashedWheelTimerを介して)タイムアウトタイマーが最初にポップした場合、非同期コールバックでエラーが発生します。

独自のローリングの非常に単純化された例は、このようなものになるかもしれません。まず、タイムアウトをスケジュールするためのオブジェクト:

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

次に、Futureを取得してタイムアウト動作を追加する関数:

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
}

ここで使用しているHashedWheelTimerはNettyのものです。

67
cmbaxter

同僚用にTimeoutFutureクラスを作成しました:

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

使用法

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

ノート:

  • プレイを想定!フレームワーク(ただし、簡単に適応できます)
  • すべてのコードは同じExecutionContextで実行されますが、これは理想的ではありません。
22
Pablo Fernandez

これらの答えにはすべて、追加の依存関係が必要です。 Java.util.Timerを使用してバージョンを作成することにしました。これは将来、関数を実行する効率的な方法です。この場合、タイムアウトをトリガーします。

詳細はこちらのブログ投稿

これをScalaのPromiseで使用すると、次のようにタイムアウトのあるFutureを作成できます。

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
  }

}
18
justinhj

PlayフレームワークにはPromise.timeoutが含まれているため、次のようなコードを記述できます

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

ライター(約束保持者)をタイムアウトロジックを制御するものにしたい場合は、次の方法で akka.pattern.after を使用します。

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

このように、約束の完了ロジックが実行されない場合、呼び出し元の未来は失敗した時点で完了します。

3
galbarm

これがScalaの標準ではないことは非常に驚きです。私のバージョンは短く、依存関係はありません

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
  }

}

使用例

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

誰も言及していないakka-streams、まだ。フローには簡単な completionTimeout メソッドがあり、これを単一ソースストリームに適用するとFutureのように機能します。

ただし、akka-streamsもキャンセルを行うため、ソースの実行を実際に終了できます。つまり、ソースにタイムアウトを通知します。

3
akauppi

将来の待機時にタイムアウトを指定できます。

ために scala.concurrent.Futureresultメソッドを使用すると、タイムアウトを指定できます。

ために scala.actors.FutureFutures.awaitAllを使用すると、タイムアウトを指定できます。

Futureの実行にタイムアウトが組み込まれているとは思わない。

3
gzm0

Monix Taskにはタイムアウトがあります サポート

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

このバージョンはタイムアウトを使用せずに動作します

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

Future IMOでタイムアウトを指定する最も簡単な方法は、scala.concurrent.Await.readyを使用するscalaの組み込みメカニズムです。これは、Futureが指定されたタイムアウトよりも長い場合にTimeoutExceptionをスローします。それ以外の場合、Future自体を返します。これは単純な考案された例です

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

Akkaシステムディスパッチャーを使用するこのバージョン(上記のPlayの例に基づく)を使用しています。

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