scala.concurrent.blocking 的不良用例?

bad use cases of scala.concurrent.blocking?

关于 this 接受的答案中的第三点,是否存在使用 blocking 进行长 - 运行 计算毫无意义或不好的情况,无论是 CPU- 还是 IO-bound,正在执行 'within' a Future?

, blocking + ForkJoinPool 如果你有很多消息要处理并且每个消息都需要长时间阻塞(这也意味着它在此期间保留了一些记忆)。 ForkJoinPool 创建新线程来补偿 "managable blocked" 线程,而不考虑 MaxThreadCount;向 VisualVm 中的数百个线程问好。而且它几乎消除了背压,因为池的队列中总是有任务的位置(如果你的背压基于 ThreadPoolExecutor 的策略)。性能被新线程分配和垃圾收集所扼杀。

所以:

  • 当消息速率不高于 1/blocking_time 时很好,因为它允许您使用线程的全部功能。一些智能背压可能有助于减慢传入消息的速度。
  • 如果某个任务在 blocking{} 期间实际使用了您的 CPU(无锁),那是没有意义的,因为它只会增加线程数,而不是系统中的实际内核数。
  • 并且对于任何其他情况都不利 - 那么您应该使用单独的固定线程池(并且可能轮询)。

P.S。 blocking 隐藏在 Await.result 中,所以并不总是很明显。在我们的项目中,有人只是在一些底层 worker actor 中做了这样的 Await

这取决于 ExecutionContext 你的 Future 被执行。

毫无意义:

如果 ExecutionContext 不是 BlockContext,那么使用 blocking 将毫无意义。也就是说,它将使用 DefaultBlockContext,它只执行代码而无需任何特殊处理。它可能不会增加那么多开销,但仍然毫无意义。

差:

Scala 的 ExecutionContext.Implicits.global 会在线程池即将耗尽时在 ForkJoinPool 中生成新线程。也就是说,如果它 知道 那将通过 blocking 发生。如果您正在生成 lots 个线程,这可能会很糟糕。如果您在短时间内排队处理大量工作,global 上下文将愉快地扩展直到陷入僵局。 @dk14 的回答对此进行了更深入的解释,但要点是它可能成为性能杀手,因为托管阻塞实际上很快就会变得难以管理。


blocking 的主要目的是避免线程池内的死锁,因此它与性能无关,因为达到死锁比产生更多线程更糟糕。但是,它绝对不是神奇的性能提升器。

我写了更多关于 blocking 的文章,特别是在