是否使用 Future.successful 和 Future.failed 从执行上下文管理的线程池中请求线程

Does use of Future.successful and Future.failed request a thread from the thread pool managed by the execution context

众所周知,在Scala中使用Future需要声明一个执行上下文,它会创建一个线程池,用于运行Future中出现的代码。我的问题是如果使用 Future.successful 或 Future.failed 会将此请求跳过到线程池。性能影响对于正确使用 Future 很重要。

为了提供一些背景知识,一位拥有多年 Scala 经验的同事告诉我,不鼓励直接使用 Future() 来包装一些代码块,因为为了有意义,Future 调用中的代码具有有可能抛出异常,这是不可取的。在我们的代码库中,直接抛出异常被认为是非功能性风格,并且还会影响性能,因为抛出异常是一种相对较慢的操作,具有性能成本,因此应该避免。我的问题分为两部分:

1) 我想知道避免直接调用 Future() 是否还有其他潜在影响:使用 Future.successful 和 Future.failed 是否通过不在线程上安排工作来节省计算资源从线程池?通过创建一个已经完成的 Future,这两个编程结构是否避免从线程池中请求线程?

具体来说,让我们比较两个代码块:

a)

val fut = Future(1+1)

b)

val fut = Future.successful(1+1)

b)不上线程池是不是节省了计算资源?

2)另外,我也想知道在映射或平面映射Future时是否从线程池中请求线程。例如,我注意到任何涉及 Future 平面图的代码都需要定义执行上下文。这是否意味着 flatmap 操作本身被安排在线程池中的线程上?是不是 Future 上的每个 map 或 flatmap 操作都发生在线程池中的线程上?在那种情况下,只要我们正在映射或平面映射 Future,就不可避免地从线程池请求线程,即使它已经由于使用 Future.successful 和 Future.failed 而完成?

具体来说,我们来看一个例子:

val fut = Future.successful(1+1).map(x => x+1)

map操作是否发生在从执行上下文管理的线程池中申请的线程中?

这只是为了让我更好地理解为什么使用 Future.successful 和 Future.failed 被认为比直接用 Future() 包装一些代码更好。重复我上面所说的,我已经知道抛出异常是性能瓶颈,但是使用Future.successful和Future.failed是否可以完全避免从线程池中请求线程从而降低性能开销?这是否完全无关紧要,因为映射或平面映射 Future 无论如何都必须在线程池中的线程上进行调度?

Future.successfulFuture.failed 不需要执行上下文,因此它们不会消耗任何线程。然而,对它们调用 map/flatMap/foreach 确实需要传入回调的执行上下文,因此确实消耗了一个线程。除了性能方面的考虑,调用 Future.successful(v) 而不是 Future(v) 更适合在语义上表明我们已经有了值 v 而我们只是将其提升为 FutureFuture.successful(1+1) 相对于 Future(1+1) 的性能提升在 real-world 系统中可能可以忽略不计,其中真正的瓶颈将很慢 I/O,例如远程 API 调用、数据库查找等