我花了一段时间学习Scala执行上下文,底层线程模型和并发的主题。你能解释一下scala.concurrent.blocking
"调整运行时行为的方式" 和"可以提高性能或避免死锁" 如scaladoc?
在the documentation中,它被表示为等待不实现Awaitable的api的手段。 (也许只是长时间运行的计算应该被包裹?)。
它实际上是做什么的?
Following through the source并不容易背叛其秘密。
答案 0 :(得分:73)
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
。按原样运行代码,您会注意到100 Future
都是立即执行的,但是如果删除blocking
,它们一次只执行几个ExecutionContext
。默认的Future
将通过生成新线程来阻止调用(标记为这样),因此不会因运行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)
}
而过载。
现在看一下这个带有4个线程的固定池的示例:
ExecutionContext
这个blocking
不是为了处理产生新线程而构建的,所以即使我的阻塞代码被Future
包围,你也可以看到它仍然只能执行最多4 {{ 1}}一次。这就是为什么我们说"可以提高性能或避免死锁" - 它不能得到保证。正如我们在后者ExecutionContext
中看到的那样,它根本得不到保证。
它是如何工作的?在链接时,blocking
执行此代码:
BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)
BlockContext.current
从当前帖子中检索BlockContext
,见here。 BlockContext
通常只是Thread
混合了BlockContext
特征。如源中所示,它存储在ThreadLocal
中,或者如果它存储在BlockContext
中。在那里找不到它,它是当前线程的模式匹配。如果当前线程不是DefaultBlockContext
,则使用blockOn
代替。
接下来,在当前BlockContext
上调用blockOn
。 BlockContext
是ExecutionContext
中的抽象方法,因此它的实现取决于BlockContext
如何处理它。如果我们查看implementation for DefaultBlockContext
(当前主题不是blockOn
时),我们会发现blocking
实际上什么也没做。因此,在非BlockContext
中使用BlockContext
意味着根本不会执行任何特殊操作,并且代码按原样运行,没有副作用。
global
s的线程怎么样?例如,在blockOn
上下文中,看到here,ForkJoinPool
做了更多。深入挖掘,您可以看到它在引擎盖下使用DefaultThreadFactory
,同一代码段中定义的ForkJoinPool
用于在blockOn
中生成新线程。如果BlockContext
(主题)没有ForkJoinPool
的实施,则blocking
不会知道您正在阻止,并且不会尝试生成更多主题作为回应。
Scala的Await
也使用{{1}}进行实施。