scala.concurrent.blocking的坏用例?

Jof*_*fer 4 concurrency scala blocking

参考这个被接受的答案中的第三点,是否有任何情况下,blocking对于长时间运行的计算,无论是CPU还是IO绑定,在"a"内执行它都是毫无意义或不好的Future

dk1*_*k14 5

从我的实践来看,如果要处理大量消息并且每个消息需要长时间阻塞(这也意味着它在此期间保留了一些内存),blocking + ForkJoinPool可能会导致线程的连续和无法控制的创建.ForkJoinPool创建新线程以补偿"可管理被阻止"的线程,无论如何MaxThreadCount; 向VisualVm中的数百个线程问好.它几乎可以杀死背压,因为在游泳池的队列中总是有一个任务地点(如果你的背压基于 ThreadPoolExecutor政策).新线程分配和垃圾收集都会破坏性能.

所以:

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

PS blocking隐藏在里面Await.result,所以它并不总是很明显.在我们的项目中,有人Await在一些潜在的工作者中做了这样的事


Mic*_*jac 5

这取决于ExecutionContextFuture正在执行的操作。

无意义:

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

坏的:

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


的主要目的blocking是避免线程池内的死锁,因此在达到死锁比产生更多线程更糟糕的意义上,它与性能密切相关。但是,它绝对不是神奇的性能增强器。

blocking这个答案中特别写了更多。