blocking
旨在作为一个提示ExecutionContext
所包含的代码正在阻塞并可能导致线程饥饿。这将使线程池有机会产生新线程以防止饥饿。这就是这个意思“调整运行时行为”。但这并不神奇,并且不适用于所有情况ExecutionContext
.
考虑这个例子:
import scala.concurrent._
val ec = scala.concurrent.ExecutionContext.Implicits.global
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
这是使用默认的全局ExecutionContext
。按原样运行代码,您会注意到 100Future
s 都会立即执行,但是如果删除blocking
,他们一次只执行几个。默认ExecutionContext
将通过生成新线程对阻塞调用(如此标记)做出反应,因此不会因运行而过载Future
s.
现在看一下这个具有 4 个线程的固定池的示例:
import java.util.concurrent.Executors
val executorService = Executors.newFixedThreadPool(4)
val ec = ExecutionContext.fromExecutorService(executorService)
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
This ExecutionContext
不是为了处理生成新线程而构建的,因此即使我的阻塞代码被blocking
,可以看到它仍然最多只执行4Future
一次。这就是为什么我们这么说“可以提高性能或避免死锁”——不能保证。正如我们在后者中看到的ExecutionContext
,根本无法保证。
它是如何工作的?如链接所示,blocking
执行这段代码:
BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)
BlockContext.current
检索BlockContext
从当前线程来看here https://github.com/scala/scala/blob/092690e7bf71bb22e6e57afb7ea7f67fdfe31a0a/src/library/scala/concurrent/BlockContext.scala#L59-65. A BlockContext
通常只是一个Thread
与BlockContext
混合了特征。如源所示,它要么存储在ThreadLocal
,或者如果在那里找不到它,则从当前线程中进行模式匹配。如果当前线程不是BlockContext
,那么DefaultBlockContext
被用来代替。
Next, blockOn
被调用当前BlockContext
. blockOn
是一个抽象方法BlockContext
,所以它的实现取决于如何ExecutionContext
处理它。如果我们看一下实施DefaultBlockContext https://github.com/scala/scala/blob/092690e7bf71bb22e6e57afb7ea7f67fdfe31a0a/src/library/scala/concurrent/BlockContext.scala#L52-54(当当前线程不是BlockContext
),我们看到blockOn
实际上那里什么也没做。所以使用blocking
在非BlockContext
意味着根本没有做任何特殊的事情,并且代码按原样运行,没有副作用。
那么线程呢BlockContext
是?例如,在global
上下文,所见here https://github.com/scala/scala/blob/092690e7bf71bb22e6e57afb7ea7f67fdfe31a0a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala#L40-56, blockOn
做得更多。深入挖掘,您可以看到它使用了ForkJoinPool
在引擎盖下,与DefaultThreadFactory
在用于生成新线程的同一代码段中定义ForkJoinPool
。如果没有实施blockOn
来自BlockContext
(线程),ForkJoinPool
不知道您正在阻塞,并且不会尝试生成更多线程作为响应。
Scala's Await https://github.com/scala/scala/blob/fdb3d96409033ea0b15a37118c423618ac00acb5/src/library/scala/concurrent/package.scala#L148也使用blocking
其实施。