Java Future にはcancel
メソッドがあり、Future
タスクを実行するスレッドを中断できます。たとえば、interruptibleブロッキング呼び出しをJava Future
でラップすると、後で中断できます。
Scala Future はcancel
メソッドを提供しません。 interruptibleブロッキング呼び出しをScala Future
でラップするとします。どうすれば中断できますか?
これはまだFuture
s 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章を参照してください。
私はこれをテストしていませんが、これは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)
}
キャンセルすることで、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)
}