首页 > 解决方案 > 带有回调和上下文切换的 scala Future

问题描述

我试图避免在我的未来地图回调上进行上下文切换。我看到 akka 有 SameThreadExecutionContext 应该处理这种类型的回调,但我不确定我是否完全理解它:

  val ec1 = ExecutionContext.fromExecutorService(...)
  val ec2 = ExecutionContext.fromExecutorService(...)

  println("0 " + Thread.currentThread().getName)
  def futureOnEc1 = Future {
    println(s"1 " + Thread.currentThread().getName)
  }(ec1)

  futureOnEc1.map { a =>
    println(s"2 " + Thread.currentThread().getName)
    a + 1
  }(AkkaSameThreadExecutionContext)

我想我会得到:

0 pool-2-thread-1
1 pool-1-thread-1
2 pool-1-thread-1

但实际结果是

0 pool-2-thread-1
1 pool-1-thread-1
2 pool-2-thread-1

我想念什么?关键是在未来的同一个线程上运行回调,而不是调用原始未来的线程。

标签: scalaasynchronousfuture

解决方案


ec1当未来尚未完成时,回调在同一个线程池中调用。通过添加Thread.sleep(1000)到您的未来身体中进行测试。

此代码确实可以按您的预期工作

  println("0 " + Thread.currentThread().getName)

  val futureOnEc1 = Future {
    Thread.sleep(1000)
    println(s"1 " + Thread.currentThread().getName)
    0
  }(ec1)

  futureOnEc1.map { a =>
    println(s"2 " + Thread.currentThread().getName)
    a + 1
  }(sameThreadExecutionContext)

印刷

0 main
1 pool-1-thread-1
2 pool-1-thread-1

但是如果未来已经完成,回调会立即由注册它的线程执行。

删除Thread.sleep并打印相同的代码

0 main
1 pool-1-thread-1
2 main

编辑:

来自的文档scala.concurrent.Future#onComplete表明了这种行为。

当这个未来通过异常或值完成时,应用提供的函数。如果未来已经完成,这将立即应用或异步安排。

并且从scala.concurrent.impl.Promise.DefaultPromise#dispatchOrAddCallback

尝试添加回调,如果已经完成,则调度要执行的回调。


推荐阅读