Scala Future 的地图是否增加了任何开销?
Does the map of Scala Future add any overhead?
假设我有这样一个函数:
import scala.concurrent._
def plus2Future(fut: Future[Int])
(implicit ec: ExecutionContext): Future[Int] = fut.map(_ + 2)
现在我正在使用 plus2Future
创建一个新的 Future
:
import import scala.concurrent.ExecutionContext.Implicits.global
val fut1 = Future { 0 }
val fut2 = plus2Future(fut1)
函数 plus2
总是 运行 现在与 fut1
在同一个线程上吗?我猜不是。
在 plus2Future
中使用 map
是否会增加线程上下文切换的开销,创建新的 Runnable
等?
Does using map in plus2Future add an overhead of thread context
switching, creating a new Runnable etc. ?
map
,Future
(通过DefaultPromise
)的默认实现是:
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity)
val p = Promise[S]()
onComplete { v => p complete (v map f) }
p.future
}
其中 onComplete
创建一个新的 CallableRunnable
并最终调用 dispatchOrAddCallback
:
/** Tries to add the callback, if already completed, it dispatches the callback to be executed.
* Used by `onComplete()` to add callbacks to a promise and by `link()` to transfer callbacks
* to the root promise when linking two promises togehter.
*/
@tailrec
private def dispatchOrAddCallback(runnable: CallbackRunnable[T]): Unit = {
getState match {
case r: Try[_] => runnable.executeWithValue(r.asInstanceOf[Try[T]])
case _: DefaultPromise[_] => compressedRoot().dispatchOrAddCallback(runnable)
case listeners: List[_] => if (updateState(listeners, runnable :: listeners)) () else dispatchOrAddCallback(runnable)
}
}
它将调用分派到底层执行上下文。这意味着由实施者 ExecutionContext
决定未来 运行 的方式和位置,因此无法给出 "It runs here or there" 的确定性答案。我们可以看到既有一个Promise
的分配,也有一个回调对象
一般来说,我不会担心这个,除非我对代码进行了基准测试并发现这是一个瓶颈。
不,它不能(确定性地)使用同一个线程(除非 EC 只有一个线程),因为两行代码之间可以经过无限量的时间。
假设我有这样一个函数:
import scala.concurrent._
def plus2Future(fut: Future[Int])
(implicit ec: ExecutionContext): Future[Int] = fut.map(_ + 2)
现在我正在使用 plus2Future
创建一个新的 Future
:
import import scala.concurrent.ExecutionContext.Implicits.global
val fut1 = Future { 0 }
val fut2 = plus2Future(fut1)
函数 plus2
总是 运行 现在与 fut1
在同一个线程上吗?我猜不是。
在 plus2Future
中使用 map
是否会增加线程上下文切换的开销,创建新的 Runnable
等?
Does using map in plus2Future add an overhead of thread context switching, creating a new Runnable etc. ?
map
,Future
(通过DefaultPromise
)的默认实现是:
def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = { // transform(f, identity)
val p = Promise[S]()
onComplete { v => p complete (v map f) }
p.future
}
其中 onComplete
创建一个新的 CallableRunnable
并最终调用 dispatchOrAddCallback
:
/** Tries to add the callback, if already completed, it dispatches the callback to be executed.
* Used by `onComplete()` to add callbacks to a promise and by `link()` to transfer callbacks
* to the root promise when linking two promises togehter.
*/
@tailrec
private def dispatchOrAddCallback(runnable: CallbackRunnable[T]): Unit = {
getState match {
case r: Try[_] => runnable.executeWithValue(r.asInstanceOf[Try[T]])
case _: DefaultPromise[_] => compressedRoot().dispatchOrAddCallback(runnable)
case listeners: List[_] => if (updateState(listeners, runnable :: listeners)) () else dispatchOrAddCallback(runnable)
}
}
它将调用分派到底层执行上下文。这意味着由实施者 ExecutionContext
决定未来 运行 的方式和位置,因此无法给出 "It runs here or there" 的确定性答案。我们可以看到既有一个Promise
的分配,也有一个回调对象
一般来说,我不会担心这个,除非我对代码进行了基准测试并发现这是一个瓶颈。
不,它不能(确定性地)使用同一个线程(除非 EC 只有一个线程),因为两行代码之间可以经过无限量的时间。