【问题标题】:Akka Scheduler: Wait for Runnable to completeAkka 调度程序:等待 Runnable 完成
【发布时间】:2020-12-04 01:01:51
【问题描述】:

所以我有以下 Akka 调度程序设置

implicit val testActorSystem = ActorSystem(Behaviors.empty, "test-actor-system")
implicit val executionContext = testActorSystem.executionContext

val cleaner: Runnable = () => //do something

val cancellable: Cancellable = testActorSystem.scheduler.scheduleOnce(0 seconds, cleaner)

我想等到我的Runnable 完成。我怎样才能检查这个?我知道这个任务在后台转换为Futures,但在这种情况下,我无法访问这些Futures 并等待它们。那么等待任务完成的最佳做法是什么?

【问题讨论】:

  • 是用于测试还是用于生产代码?
  • @IvanStanislavciuc,这有关系吗?
  • 请注意,“这个任务在后台被转换为Futures”是不正确的:不需要发生这样的转换(参见,例如LightArrayRevolverScheduler,它通过传递它来执行Runnable通过TaskHolderExecutionContext 中执行)。
  • 在您的代码的特定情况下,如果您希望在不久的将来(在0 seconds)执行任务并获得在任务完成时完成的Future,为什么不Future { cleaner.run() }?

标签: scala akka scheduler


【解决方案1】:

假设您可以控制Runnable,您可以让Runnable 完成PromiseFuture

import akka.Done
import scala.concurrent.Promise
import scala.util.control.NonFatal

val (cleaner, cleanerDoneFut) = {
  val donePromise = Promise[Done]

  val cleaner: Runnable = () => {
    try {
      // do stuff
      donePromise.success(Done)
    } catch {
      case NonFatal(e) => donePromise.failure(e)
    }
  }

  cleaner -> donePromise.future
}

val cancellable = ...

显然,这只能通过您希望只执行一次的Runnable 来完成。

请注意,您可以将任何 Runnable 包装到 Runnable 中,从而在完成时完成 Future

// the future will contain the result of the first time the returned runnable runs
def runnableWithCompletionFuture(runnable: Runnable): (Runnable, Future[Done]) = {
  val donePromise = Promise[Done]

  val wrapped = try {
    runnable.run()
    donePromise.success(Done)
  } catch {
    case NonFatal(e) => donePromise.failure(e)
  }

  wrapped -> donePromise.future
}

def scheduleWithCompletion(when: FiniteDuration)(r: Runnable)(implicit actorSystem: ActorSystem): (Cancellable, Future[Done]) = {
  val (wrapped, fut) = runnableWithCompletionFuture(r)
  val c = actorSystem.scheduler.scheduleOnce(when, wrapped)
  c -> fut
}

通过挖掘实现,没有这样的技巧,没有办法判断任意预定的Runnable是否已经完成。

【讨论】:

  • 不幸的是,这改变了Runnable 的实现,所以我不能真正使用它。
【解决方案2】:

我认为你做不到。你拨打method

def scheduleOnce(delay: FiniteDuration, runnable: Runnable)(implicit executor: ExecutionContext): Cancellable

Cancellable 是:

trait Cancellable {

  /**
   * Cancels this Cancellable and returns true if that was successful.
   * If this cancellable was (concurrently) cancelled already, then this method
   * will return false although isCancelled will return true.
   *
   * Java & Scala API
   */
  def cancel(): Boolean

  /**
   * Returns true if and only if this Cancellable has been successfully cancelled
   *
   * Java & Scala API
   */
  def isCancelled: Boolean
}

所以我认为没有这样的选择。

【讨论】:

  • 我试图将其与 Java 的 ExecutorService 联系起来。使用此服务,您仍然可以安排任务并等待其终止 - awaitTermination。那么您知道不提供此类功能的调度程序背后的基本原理是什么吗?主线程退出时调度器做了什么?它会等待生成的线程完成吗?
  • @Niko,jvm 等待所有正在运行的线程完成。如果需要,可以添加关闭挂钩,并取消正在运行的作业。
  • Akka 调度程序的预期用途是延迟非常短的任务(想想亚毫秒),规范的预期任务是将一条或多条消息发送给一个或多个参与者。 (事实上​​,曾引用 Viktor Klang 的话说,它应该被称为 Deferer,而不是 Scheduler)。它并不是真正用于执行一般任务。
  • @LeviRamsey 能否请您指出一些 Akka 官方文档,这些文档中提到了这种“短促”的任务。当然,通过阅读the these docs here,可以清楚地看出调度程序不适用于长期任务
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-20
相关资源
最近更新 更多