scala.concurrent.blocking - 它实际上是做什么的? [英] scala.concurrent.blocking - what does it actually do?

查看:44
本文介绍了scala.concurrent.blocking - 它实际上是做什么的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我花了一段时间学习 Scala 执行上下文、底层线程模型和并发性的主题.你能解释一下 scala.concurrent.blocking 调整运行时行为"可以提高性能或避免死锁" 所描述的哪些方式吗?在 scaladoc?

I have spent a while learning the topic of Scala execution contexts, underlying threading models and concurrency. Can you explain in what ways does scala.concurrent.blocking "adjust the runtime behavior" and "may improve performance or avoid deadlocks" as described in the scaladoc?

文档中,它被呈现为一种等待 api 的方法,它不会实现等待.(也许也应该包装长时间运行的计算?).

In the documentation, it is presented as a means to await api that doesn't implement Awaitable. (Perhaps also just long running computation should be wrapped?).

它实际上是做什么的?

遵循源代码轻易泄露它的秘密.

推荐答案

blocking 旨在提示 ExecutionContext 所包含的代码正在阻塞并且可以导致线程饥饿.这将使线程池有机会产生新线程以防止饥饿.这就是调整运行时行为"的含义.但这并不神奇,并且不适用于每个 ExecutionContext.

blocking is meant to act as a hint to the ExecutionContext that the contained code is blocking and could lead to thread starvation. This will give the thread pool a chance to spawn new threads in order to prevent starvation. This is what is meant by "adjust the runtime behavior". It's not magic though, and won't work with every 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 s 而过载.

This is using the default global ExecutionContext. Running the code as-is, you will notice that the 100 Futures are all executed immediately, but if you remove blocking, they only execute a few at a time. The default ExecutionContext will react to blocking calls (marked as such) by spawning new threads, and thus doesn't get overloaded with running Futures.

现在看看这个有 4 个线程的固定池的例子:

Now look at this example with a fixed pool of 4 threads:

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)
}

这个 ExecutionContext 不是用来处理产生新线程的,所以即使我的阻塞代码被 blocking 包围,你可以看到它仍然只会在一次最多 4 个 Future .这就是为什么我们说它可能会提高性能或避免死锁"——这并不能保证.正如我们在后面的 ExecutionContext 中看到的,它根本无法保证.

This ExecutionContext isn't built to handle spawning new threads, and so even with my blocking code surrounded with blocking, you can see that it will still only execute at most 4 Futures at a time. And so that's why we say it "may improve performance or avoid deadlocks"--it's not guaranteed. As we see in the latter ExecutionContext, it's not guaranteed at all.

它是如何工作的?作为链接,blocking 执行此代码:

How does it work? As linked, blocking executes this code:

BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)

BlockContext.current 从当前线程中检索BlockContext,见这里.BlockContext 通常只是一个 Thread 混合了 BlockContext trait.正如在源代码中看到的那样,它要么存储在 ThreadLocal,或者如果在那里没有找到,则它是当前线程之外的模式匹配.如果当前线程不是 BlockContext,则使用 DefaultBlockContext 代替.

BlockContext.current retrieves the BlockContext from the current thread, seen here. A BlockContext is usually just a Thread with the BlockContext trait mixed in. As seen in the source, it is either stored in a ThreadLocal, or if it's not found there, it is pattern matched out of the current thread. If the current thread is not a BlockContext, then the DefaultBlockContext is used instead.

接下来,在当前 BlockContext 上调用 blockOn.blockOnBlockContext 中的一个抽象方法,所以它的实现依赖于ExecutionContext 如何处理它.如果我们查看 DefaultBlockContext(当当前线程不是 BlockContext 时),我们看到 blockOn 实际上什么都不做.因此,在非BlockContext 中使用blocking 意味着根本没有做任何特别的事情,并且代码按原样运行,没有副作用.

Next, blockOn is called on the current BlockContext. blockOn is an abstract method in BlockContext, so it's implementation is dependent on how the ExecutionContext handles it. If we look at the implementation for DefaultBlockContext (when the current thread is not a BlockContext), we see that blockOn actually does nothing there. So using blocking in a non-BlockContext means that nothing special is done at all, and the code is run as-is, with no side-effects.

BlockContext 的线程怎么样?例如,在 global 上下文中,看到 这里blockOn 做得更多.深入挖掘,您可以看到它在底层使用了 ForkJoinPool,在同一代码段中定义的 DefaultThreadFactory 用于在 ForkJoinPool<中生成新线程/代码>.如果没有 BlockContext(线程)中的 blockOn 的实现,ForkJoinPool 不知道你正在阻塞,也不会尝试产生更多线程作为响应.

What about threads that are BlockContexts? For instance, in the global context, seen here, blockOn does quite a bit more. Digging deeper, you can see that it's using a ForkJoinPool under the hood, with the DefaultThreadFactory defined in the same snippet being used for spawning new threads in the ForkJoinPool. Without the implementation of blockOn from the BlockContext (thread), the ForkJoinPool doesn't know you're blocking, and won't try to spawn more threads in response.

Scala 的 同样使用 blocking 来实现.

Scala's Await too, uses blocking for its implementation.

这篇关于scala.concurrent.blocking - 它实际上是做什么的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆