web-dev-qa-db-ja.com

ScalaでFutureをキャンセルする方法は?

Java Future にはcancelメソッドがあり、Futureタスクを実行するスレッドを中断できます。たとえば、interruptibleブロッキング呼び出しをJava Futureでラップすると、後で中断できます。

Scala Futurecancelメソッドを提供しません。 interruptibleブロッキング呼び出しをScala Futureでラップするとします。どうすれば中断できますか?

52
Michael

これはまだFutures APIの一部ではありませんが、将来的に拡張機能として追加される可能性があります。

回避策として、firstCompletedOfを使用して、キャンセルしたい未来とカスタムPromiseからの未来の2つの未来をラップできます。その後、約束を破って、こうして作成された未来をキャンセルできます。

def cancellable[T](f: Future[T])(customCode: => Unit): (() => Unit, Future[T]) = {
  val p = Promise[T]
  val first = Future firstCompletedOf Seq(p.future, f)
  val cancellation: () => Unit = {
    () =>
      first onFailure { case e => customCode}
      p failure new Exception
  }
  (cancellation, first)
}

今後、これを「キャンセル可能なラッパー」を取得するために呼び出すことができます。ユースケースの例:

val f = callReturningAFuture()
val (cancel, f1) = cancellable(f) {
  cancelTheCallReturningAFuture()
}

// somewhere else in code
if (condition) cancel() else println(Await.result(f1))

編集:

キャンセルの詳細については、 Scalaでの並行プログラミングの学習 本の第4章を参照してください。

30
axel22

私はこれをテストしていませんが、これはPablo FranciscoPérezHidalgoの答えを拡張しています。 Java Futureを待つのをブロックする代わりに、代わりに中間のPromiseを使用します。

import Java.util.concurrent.{Callable, FutureTask}
import scala.concurrent.{ExecutionContext, Promise}
import scala.util.Try

class Cancellable[T](executionContext: ExecutionContext, todo: => T) {
  private val promise = Promise[T]()

  def future = promise.future

  private val jf: FutureTask[T] = new FutureTask[T](
    new Callable[T] {
      override def call(): T = todo
    }
  ) {
    override def done() = promise.complete(Try(get()))
  }

  def cancel(): Unit = jf.cancel(true)

  executionContext.execute(jf)
}

object Cancellable {
  def apply[T](todo: => T)(implicit executionContext: ExecutionContext): Cancellable[T] =
    new Cancellable[T](executionContext, todo)
}
9
nightingale

キャンセルすることで、futureを激しく中断したいと思います。

コードのこのセグメントを見つけました: https://Gist.github.com/viktorklang/5409467

いくつかのテストを行い、正常に動作するようです!

楽しい :)

Java 7 Future interface とその実装を利用することで、実装の複雑さを軽減できると思います。

Cancellableは、そのcancelメソッドによってキャンセルされるJava futureを構築できます。別の未来は、その完了を待つことができます。したがって、状態自体は不変である監視可能なインターフェースになります。

 class Cancellable[T](executionContext: ExecutionContext, todo: => T) {

   private val jf: FutureTask[T] = new FutureTask[T](
     new Callable[T] {
       override def call(): T = todo
     }
   )

   executionContext.execute(jf)

   implicit val _: ExecutionContext = executionContext

   val future: Future[T] = Future {
     jf.get
   }

   def cancel(): Unit = jf.cancel(true)

 }

 object Cancellable {
   def apply[T](todo: => T)(implicit executionContext: ExecutionContext): Cancellable[T] =
     new Cancellable[T](executionContext, todo)
 }