Java Future a la méthode cancel
, qui peut interrompre le thread, qui exécute la tâche Future
. Par exemple, si j'encapsule un appel de blocage interruptible dans un Java Future
Je peux l'interrompre plus tard.
Scala Future ne fournit aucune méthode cancel
. Supposons que j'encapsule un appel interruptible bloquant dans un Scala Future
. Comment puis-je l'interrompre?
Cela ne fait pas encore partie de l'API Future
s, mais pourrait être ajouté en tant qu'extension à l'avenir.
Comme solution de contournement, vous pouvez utiliser le firstCompletedOf
pour envelopper 2 futures - le futur que vous souhaitez annuler et un futur qui provient d'un Promise
personnalisé. Vous pouvez alors annuler le futur ainsi créé en manquant à la promesse:
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)
}
Vous pouvez maintenant l'appeler à tout moment pour obtenir un "wrapper annulable". Exemple de cas d'utilisation:
val f = callReturningAFuture()
val (cancel, f1) = cancellable(f) {
cancelTheCallReturningAFuture()
}
// somewhere else in code
if (condition) cancel() else println(Await.result(f1))
ÉDITER:
Pour une discussion détaillée sur l'annulation, voir le chapitre 4 du livre Apprentissage de la programmation simultanée dans Scala .
Je n'ai pas testé cela, mais cela élargit la réponse de Pablo Francisco Pérez Hidalgo. Au lieu de bloquer en attendant le Java Future
, nous utilisons à la place un Promise
intermédiaire.
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)
}
En annulant, je suppose que vous souhaitez interrompre violemment le future
.
Trouvé ce segment de code: https://Gist.github.com/viktorklang/5409467
A fait quelques tests et semble bien fonctionner!
Prendre plaisir :)
Je pense qu'il est possible de réduire la complexité des implémentations fournies en utilisant Java 7 Future
interface et ses implémentations.
Cancellable
peut construire un Java futur qui est celui qui doit être annulé par sa méthode cancel
. Un autre futur peut attendre sa fin, devenant ainsi l'interface observable qui est lui-même immuable dans l'état:
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)
}