【发布时间】:2015-04-16 00:42:00
【问题描述】:
参考this 接受的答案中的第三点,是否有任何情况下使用blocking 进行长时间运行的计算(无论是 CPU 还是 IO 绑定)是没有意义或不好的,即在Future'内'执行?
【问题讨论】:
标签: scala concurrency blocking
参考this 接受的答案中的第三点,是否有任何情况下使用blocking 进行长时间运行的计算(无论是 CPU 还是 IO 绑定)是没有意义或不好的,即在Future'内'执行?
【问题讨论】:
标签: scala concurrency blocking
这取决于您的Future 正在执行的ExecutionContext。
毫无意义:
如果ExecutionContext 不是BlockContext,那么使用blocking 将毫无意义。也就是说,它将使用DefaultBlockContext,它只执行代码而无需任何特殊处理。它可能不会增加那么多开销,但仍然没有意义。
不好:
当线程池即将耗尽时,Scala 的ExecutionContext.Implicits.global 会在ForkJoinPool 中生成新线程。也就是说,如果它知道这将通过blocking 发生。如果您要生成 很多 个线程,这可能会很糟糕。如果您在短时间内排队处理大量工作,global 上下文将很高兴地扩展直到陷入僵局。 @dk14 的回答更深入地解释了这一点,但要点是它可能会成为性能杀手,因为托管阻塞实际上很快就会变得难以管理。
blocking 的主要目的是避免线程池中的死锁,因此它与性能密切相关,因为达到死锁会比产生更多线程更糟糕。但是,它绝对不是一个神奇的性能增强器。
我写过更多关于blocking 的文章,尤其是this answer。
【讨论】:
ForkJoinPool之外的任何其他阻塞感知实现?我没找到……
From my practice, blocking + ForkJoinPool 如果您有大量消息要处理并且每条消息都需要长时间阻塞(这也意味着它在此期间持有一些内存),可能会导致线程的连续和无法控制的创建. ForkJoinPool 创建新线程来补偿“可管理阻塞”线程,不管MaxThreadCount;向 VisualVm 中的数百个线程问好。而且它几乎可以消除背压,因为池队列中总是有一个任务位置(如果您的背压基于ThreadPoolExecutor 的策略)。新线程分配和垃圾回收都会扼杀性能。
所以:
blocking{}(无锁)期间实际使用您的 CPU 是没有意义的,因为它只会增加线程数而不是系统中的实际内核数。 附: blocking 隐藏在 Await.result 中,因此并不总是很明显。在我们的项目中,有人只是在一些底层的工人演员中做了这样的Await。
【讨论】: