scala - 带有回调和上下文切换的 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
我想念什么?关键是在未来的同一个线程上运行回调,而不是调用原始未来的线程。
解决方案
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
尝试添加回调,如果已经完成,则调度要执行的回调。
推荐阅读
- spring - 如何有效地将大量数据从数据库传输到客户端用户 Spring REST
- visual-studio-code - 在当前窗口中使用 `code.` 打开 VSCode,而不是打开一个新窗口?
- reactjs - React 中的 AWS Amplify UI 自定义
- typescript - 点燃元素打字稿反映:真
- javascript - 在 React Native 中使用自定义文本时出现内存泄漏
- couchdb - 使用多索引排序查询 Hyperledger Fabric 中的状态级别数据库
- r - 根据逻辑条件从数据框中提取单个值,Tidyverse 风格
- node.js - 使用 NodeJS 在测试中从磁盘读取 - 同步与异步
- r - glmer使用哪个家庭?
- python - 在 TensorFlow 中手动设置梯度值并在反向传播中使用它们