调用方Task如何避免FlatMap结束的子任务更改其调度程序

时间:2018-06-22 23:01:19

标签: scala task scheduler monix

我正在寻找一种方法,Task(即外部作用域)可以使用flatMap或类似方法执行“ subTask”,并确保外部作用域中的所有后续链接调用使用原始的调度程序。

使用的库和scala:

  • scala-2.12.4
  • monix-"io.monix" %% "monix" % "3.0.0-RC1"
  • 猫-"org.typelevel" %% "cats-core" % "1.0.1"

示例代码:

import monix.eval.Task
import monix.execution.Scheduler
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import monix.execution.Scheduler.Implicits.global
import cats.implicits._

object Test extends App {
  val io1 = Scheduler.io("io1")
  val io2 = Scheduler.io("io2")

  def taskEval(name: String) = Task.eval(println(s"Running eval Task [$name] on thread [${Thread.currentThread().getName}]"))

  def subTask: Task[Unit] = {
    taskEval("subTaskScope").executeOn(io2)
  }

  def outerScope(sub: Task[Unit]): Task[Unit] = {
    taskEval("outerScopeBefore") *> sub *> taskEval("outerScopeAfter")
  }

  def outerScopeTryProtect(sub: Task[Unit]): Task[Unit] = {
    taskEval("outerScopeBefore") *> (sub <* Task.shift) *> taskEval("outerScopeAfter")
  }

  val program1 = taskEval("programBefore").executeOn(io1) *> outerScope(subTask) *> taskEval("programAfter")
  val program2 = taskEval("programBefore").executeOn(io1) *> outerScopeTryProtect(subTask) *> taskEval("programAfter")

  Await.result(program1.runAsync, Duration.Inf)
// Running eval Task [programBefore] on thread [io1-573]
// Running eval Task [outerScopeBefore] on thread [io1-573]
// Running eval Task [subTaskScope] on thread [io2-574]
// Running eval Task [outerScopeAfter] on thread [io2-574] // << we don't shift back so we are stuck with the scheduler that is forces by subTask
// Running eval Task [programAfter] on thread [io2-574]

  println("------")
// Running eval Task [programBefore] on thread [io1-573]
// Running eval Task [outerScopeBefore] on thread [io1-573]
// Running eval Task [subTaskScope] on thread [io2-574]
// Running eval Task [outerScopeAfter] on thread [scala-execution-context-global-575] // we shift the scheduler but this restores the default scheduler
// Running eval Task [programAfter] on thread [scala-execution-context-global-575]

  Await.result(program2.runAsync, Duration.Inf)
}

subTask方法希望在专用调度程序(io2)上执行一些异步工作,因此它使用executeOn强制调度程序异步边界。

outerScope方法正在某个程序program1中执行,它使用sub调用subTask(即flatMap)。由于它没有任何明确的异步边界,因此如果subTask恰巧更改了调度程序(确实如此),则其余outerScope会使用subTask更改的调度程序。因此,在taskEval("outerScopeAfter")调度程序上执行对io2的调用。

outerScopeTryProtect通过在Task.shift踏入flatMap(即sub)之后引入异步边界(使用subTask)来尝试保护其使用的调度程序。但是,异步边界(Task.shift)将调度程序重置为默认调度程序,在这种情况下,它将一路退回到program2.runAsync中隐式使用的调度程序。这不是我们想要的,因为我们想回到调用taskEval("outerScopeBefore")时使用的调度程序,即调度程序io1

我正在寻找的是类似Task[A].flatMap[B](f: A => Task[B]): Task[B]的东西,它将以f指定的任何方式执行f产生的任务(可能使用其他调度程序),但是结果Task调用中的flatMap将返回到Task[A]之前由flatMap使用的调度程序。

0 个答案:

没有答案