57 votes

Comment annuler Future en Scala ?

Java Future a la méthode cancel, qui peut interrompre le thread exécutant la tâche Future. Par exemple, si j'emballe un appel bloquant interruptible dans un Java Future, je peux l'interrompre plus tard.

Scala Future ne fournit pas de méthode cancel. Supposons que j'emballe un appel bloquant interruptible dans un Scala Future. Comment puis-je l'interrompre ?

33voto

axel22 Points 17400

Ceci n'est pas encore une partie de l'API des Future, mais pourrait être ajouté comme une extension à l'avenir.

En attendant, vous pourriez utiliser le firstCompletedOf pour envelopper 2 futures - le future que vous souhaitez annuler et un future provenant d'une Promise personnalisée. Vous pourriez ensuite annuler le future ainsi créé en échouant 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)
}

Maintenant, vous pouvez appeler ceci sur n'importe quel future pour obtenir un "wrapper annulable". Exemple d'utilisation :

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

// quelque part ailleurs dans le code
if (condition) cancel() else println(Await.result(f1))

EDIT :

Pour une discussion détaillée sur l'annulation, consultez le Chapitre 4 dans le livre Learning concurrent programming in Scala.

3 votes

Oui, les Promises sont l'extrémité de production des futures en Scala, c'est là que vous contrôlez le résultat. Quelques liens : scala-lang.org/api/current/index.html#scala.concurrent.Promi‌​se, docs.scala-lang.org/overviews/core/futures.html#promises

0 votes

Non, vous devez ajouter ce morceau de logique personnalisée d'annulation/interruption dans le customCode ci-dessus.

1 votes

Merci. Pourquoi la bibliothèque standard Scala n'inclut-elle pas ce cancel ?

11voto

nightingale Points 671

Je n'ai pas testé cela, mais cela développe la réponse de Pablo Francisco Pérez Hidalgo. Au lieu de bloquer en attendant le java Future, nous utilisons plutôt une 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)
}

0 votes

Superbe optimisation! Plus de code de style Java mais la réduction de l'utilisation des threads en vaut la peine. J'adore les synergies de Stack Overflow

1 votes

@PabloFranciscoPérezHidalgo Aurais-tu un problème si je publie une adaptation de ceci en tant que bibliothèque? Tu peux trouver mon adaptation ici

0 votes

@NthPortal Considérez-le open source. Donc, n'hésitez pas! Ce serait bien si la paternité de l'idée était incluse dans le repo :D (Vous pourriez inclure mon nom d'utilisateur Twitter "pfcoperez" ou mon lien de profil Stack Overflow - ainsi que celui de nightingale -)

8voto

George Pligor Points 503

En annulant, je suppose que vous voudriez interrompre violemment le future.

J'ai trouvé ce segment de code : https://gist.github.com/viktorklang/5409467

J'ai fait quelques tests et cela semble bien fonctionner !

Profitez-en :)

0 votes

Avez-vous votre segment de code utilisant cette fonction? J'ai du mal à comprendre comment l'utiliser.

0 votes

Il vous suffit d'appeler la fonction. Elle renvoie deux valeurs, le futur et le cancellor (une fonction que vous pouvez appeler pour annuler le futur en cours). Il n'y a rien de plus à comprendre vraiment. Vous n'avez pas besoin de comprendre pour l'utiliser. Il suffit de copier-coller. J'espère que cela vous aidera un peu.

1 votes

La réponse ici pourrait aider un peu à comprendre comment utiliser ceci: stackoverflow.com/a/16050595/237399

3voto

Je pense qu'il est possible de réduire la complexité des implémentations en faisant usage de l'interface Future de Java 7 et de ses implémentations.

Cancellable peut construire un futur Java qui peut être annulé par sa méthode cancel. Un autre futur peut attendre sa complétion devenant ainsi l'interface observable qui est en elle-même immuable dans son é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)
 }

1 votes

Je n'ai pas encore testé cela, mais ça a l'air génial. Mais peut-être que l'appel jf.get devrait être enveloppé dans un bloc scala.concurrent.blocking par mesure de précaution.

0 votes

@nightingale Merci! Je l'utilise en production github.com/Stratio/Common-utils/blob/master/src/main/scala/c‌​​om/… at github.com/Stratio/Crossdata/blob/…. jf.get est appelé dans un bloc passé à un constructeur Future, à mon avis il n'y a pas de danger de blocage potentiel en effet, non?

1 votes

@nightingale J'ai vérifié cette question stackoverflow.com/questions/19681389/… et cela (blocking) semble peut-être s'appliquer. Je vais vérifier, merci pour votre suggestion :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X