还有一个方面的期货我从官方教程参考中并不完全理解。 http://docs.scala-lang.org/overviews/core/futures.html
scala中的期货是否具有某种内置的超时机制?假设下面的示例是一个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)
}
答案 0 :(得分:66)
使用阻止获取Future
的结果时,只会出现超时行为。如果您想使用非阻止回调onComplete
,onSuccess
或onFailure
,那么您必须滚动自己的超时处理。 Akka内置了针对actor之间的请求/响应(?
)消息传递的超时处理,但不确定是否要开始使用Akka。 FWIW,在Akka中,对于超时处理,它们通过Futures
组成两个Future.firstCompletedOf
,一个表示实际的异步任务,另一个表示超时。如果超时计时器(通过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。
答案 1 :(得分:22)
我刚为同事创建了一个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
中运行,这可能并不理想。答案 2 :(得分:16)
所有这些答案都需要额外的依赖关系。我决定使用java.util.Timer编写一个版本,这是一种在将来运行函数的有效方法,在这种情况下会触发超时。
Blog post with more details here
将此与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
}
}
答案 3 :(得分:5)
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))
}
答案 4 :(得分:3)
您可以在等待将来时指定超时:
对于scala.concurrent.Future
,result
方法可让您指定超时。
对于scala.actors.Future
,Futures.awaitAll
可让您指定超时。
我不认为执行未来会内置超时。
答案 5 :(得分:3)
我很惊讶这不是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
答案 6 :(得分:3)
还没有人提到akka-streams
。这些流有一个简单的completionTimeout
方法,将其应用于单一源流就像Future一样。
但是,akka-streams也会取消,因此它实际上可以结束源的运行,即它向源发出超时信号。
答案 7 :(得分:3)
如果您希望编写者(承诺持有者)成为控制超时逻辑的人,请按以下方式使用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))
这样,如果您的承诺完成逻辑永远不会发生,那么您的来电者的未来仍将在某个时刻失败完成。
答案 8 :(得分:1)
Monix Task
已超时support
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)
答案 9 :(得分:0)
我使用此版本(基于上面的Play示例),该版本使用Akka系统调度程序:
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
}
}
答案 10 :(得分:0)
在Future IMO上指定超时的最简单方法是使用scala.concurrent.Await.ready
的scala内置机制,如果Future花费的时间比指定的超时时间长,则会抛出TimeoutException
。否则,它将返回未来本身。
这是一个简单的示例
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)
答案 11 :(得分:0)
您可以通过使用Await
等待未来。
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}
val meaningOfLife: Int = Await.result(Future(42), 1.nano)
println (meaningOfLife)
以上打印42
在这种情况下,您可能需要一个隐式ExecutionContext
,只需添加:
import scala.concurrent.ExecutionContext.Implicits.global
另一种方法是使用monix中的Coeval
。此方法并非在所有情况下都适用,您可以阅读关于它的所有内容here。
基本思想是,有时future并不会真正花费任何时间,并且会返回同步函数调用或值的结果,因此可以在当前线程上评估future。这对于测试和模拟期货也很有用。另外,您不必指定预期的超时时间,但不必担心会感到超时。
您首先将未来转变为Task
,然后将任务包装在Coeval
中,然后等着看得到的结果时,请双手合十。这是一个非常简单的示例来说明其工作原理:
您需要隐式Scheduler
才能使用它:
import monix.execution.Scheduler.Implicits.global
Coeval(Task.fromFuture(Future (42)).runSyncStep).value() match {
case Right(v) => println(v)
case Left(task) => println("Task did not finish")
}
以上操作完成并将42
打印到控制台。
Coeval(Task.fromFuture(Future {
scala.concurrent.blocking {
42
}
}).runSyncStep).value() match {
case Right(v) => println(v)
case Left(task) => println("Task did not finish")
}
此示例打印Task did not finish
:
答案 12 :(得分:0)
Numba
答案 13 :(得分:-1)
此版本无需超时即可工作
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}"))
}
}