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