假设我有一个函数,它调用阻塞可中断操作。我想以超时的方式异步运行它。也就是说,我想在超时到期时中断该功能。
所以我想尝试这样的事情:
import scala.util.Try import scala.concurrent.Future def launch(f: () => Unit, timeout: Int): Future[Try[Unit]] = { val aref = new java.util.concurrent.atomic.AtomicReference[Thread]() import ExecutionContext.Implicits.global Future {Thread.sleep(timeout); aref.get().interrupt} // 1 Future {aref.set(Thread.currentThread); Try(f())} // 2 }
问题是(1)中的aref
可以为null,因为(2)尚未将其设置为当前线程。在这种情况下,我想等到aref
设置完毕。最好的方法是什么?
答案 0 :(得分:16)
您可以使用Await稍微简单一些。 Await.result
方法将超时持续时间作为第二个参数,并在超时时抛出TimeoutException
。
try {
import scala.concurrent.duration._
Await.result(aref, 10 seconds);
} catch {
case e: TimeoutException => // whatever you want to do.
}
答案 1 :(得分:6)
我也需要相同的行为,所以这就是我解决它的方式。我基本上创建了一个创建计时器的对象,如果未来在指定的持续时间内没有完成,则会使用TimeoutException失败。
package mypackage
import scala.concurrent.{Promise, Future}
import scala.concurrent.duration.FiniteDuration
import akka.actor.ActorSystem
import scala.concurrent.ExecutionContext.Implicits.global
object TimeoutFuture {
val actorSystem = ActorSystem("myActorSystem")
def apply[A](timeout: FiniteDuration)(block: => A): Future[A] = {
val promise = Promise[A]()
actorSystem.scheduler.scheduleOnce(timeout) {
promise tryFailure new java.util.concurrent.TimeoutException
}
Future {
try {
promise success block
}
catch {
case e:Throwable => promise failure e
}
}
promise.future
}
}
答案 2 :(得分:5)
如果添加CountDownLatch
,您可以实现所需的行为。 (请注意,阻塞(即卡在await
)很多很多Future
可能会导致线程池不足。)
import scala.util.Try
import scala.concurrent.Future
def launch(f: () => Unit, timeout: Int): Future[Try[Unit]] = {
val aref = new java.util.concurrent.atomic.AtomicReference[Thread]()
val cdl = new java.util.concurrent.CountDownLatch(1)
import ExecutionContext.Implicits.global
Future {Thread.sleep(timeout); cdl.await(); aref.get().interrupt} // 1
Future {aref.set(Thread.currentThread); cdl.countDown(); Try(f())} // 2
}
答案 3 :(得分:3)
虽然你已经得到了一些关于如何通过阻止额外线程来处理超时来实现它的答案,但我建议你尝试不同的方式,因为Rex Kerr已经给出了。我不知道你在f()
做了什么,但如果它是I / O绑定的,我建议你只使用异步I / O库。如果它是某种循环,你可以将超时值直接传递给该函数,并在那里抛出TimeoutException
,如果它超过了超时。例如:
import scala.concurrent.duration._
import java.util.concurrent.TimeoutException
def doSth(timeout: Deadline) = {
for {
i <- 0 to 10
} yield {
Thread.sleep(1000)
if (timeout.isOverdue)
throw new TimeoutException("Operation timed out.")
i
}
}
scala> future { doSth(12.seconds.fromNow) }
res3: scala.concurrent.Future[scala.collection.immutable.IndexedSeq[Int]] =
scala.concurrent.impl.Promise$DefaultPromise@3d104456
scala> Await.result(res3, Duration.Inf)
res6: scala.collection.immutable.IndexedSeq[Int] =
Vector(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> future { doSth(2.seconds.fromNow) }
res7: scala.concurrent.Future[scala.collection.immutable.IndexedSeq[Int]] =
scala.concurrent.impl.Promise$DefaultPromise@f7dd680
scala> Await.result(res7, Duration.Inf)
java.util.concurrent.TimeoutException: Operation timed out.
at $anonfun$doSth$1.apply$mcII$sp(<console>:17)
at $anonfun$doSth$1.apply(<console>:13)
at $anonfun$doSth$1.apply(<console>:13)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
...
scala> res7.value
res10: Option[scala.util.Try[scala.collection.immutable.IndexedSeq[Int]]] =
Some(Failure(java.util.concurrent.TimeoutException: Operation timed out.))
这只会使用1个线程,它将在超时+单步执行时间后终止。
答案 4 :(得分:1)
你也可以尝试使用这样的CountDownLatch
:
def launch(f: () => Unit, timeout: Int): Future[Try[Unit]] = {
val aref = new java.util.concurrent.atomic.AtomicReference[Thread]()
import ExecutionContext.Implicits.global
val latch = new CountDownLatch(1)
Future {
latch.await()
aref.get().interrupt
}
Future {
aref.set(Thread.currentThread)
latch.countDown()
Try(f())
}
}
现在,我打电话给latch.await()
,我永远等待,但你当然可以改为:
latch.await(1, TimeUnit.SECONDS)
然后用Try
换行以处理是否/如果超时。