Что касается третьего пункта в принятом ответе this, существуют ли случаи, для которых было бы бессмысленно или плохо использовать blocking
для долговременного вычисления, будь то ЦПУ или IO-связанный, который выполняется «внутри», a Future
?Плохие примеры использования scala.concurrent.blocking?
ответ
Это зависит от того, ExecutionContext
ваш Future
в настоящее время выполняется в
Pointless:.
Если ExecutionContext
не BlockContext
, то с помощью blocking
будет бессмысленно. То есть, он будет использовать DefaultBlockContext, который просто выполняет код без какой-либо специальной обработки. Вероятно, это не добавило бы слишком много накладных расходов, но тем не менее бессмысленно.
Bad:
в Scala ExecutionContext.Implicits.global
сделан на нерест новые потоки в ForkJoinPool
когда пул потоков собирается быть исчерпаны. То есть, если знает, что должно произойти через blocking
. Это может быть плохо, если вы нерест лотов тем. Если в течение короткого промежутка времени вы заработаете много работы, контекст global
будет радостно расширяться до тупика. Ответ @ dk14 объясняет это более подробно, но суть в том, что это может быть убийца производительности, поскольку управляемая блокировка может стать быстро неуправляемой.
Основная цель blocking
, чтобы избежать тупиков внутри пулов потоков, так что косвенное отношение к производительности в том смысле, что достижение тупика будет хуже, чем нерест несколько больше нитей. Тем не менее, это определенно не волшебный усилитель производительности.
Я писал больше о blocking
, в частности, в this answer.
From my practice, blocking
+ ForkJoinPool
может привести к contionuous и неконтролируемому созданию потоков, если у вас есть много сообщений, чтобы обработать и каждый из них требует длительной блокировки (что также означает, что она имеет некоторую память во время таких). ForkJoinPool
создает новый поток для компенсации «управляемой блокировки», независимо от MaxThreadCount
; приветствуйте сотни потоков в VisualVm. И это почти убивает противодавление, поскольку в очереди пула всегда есть место для задачи (если ваше противодавление основано на политике ThreadPoolExecutor
). Производительность становится убиваемой как при сборе новых потоков, так и в сборке мусора.
Итак:
- это хорошо, когда скорость сообщения не намного выше, чем 1/blocking_time, поскольку это позволяет использовать полную мощность потоков. Некоторое интеллектуальное противодавление может помочь замедлить входящие сообщения.
- Бессмысленно, если задача фактически использует ваш процессор во время
blocking{}
(без блокировок), так как он просто увеличит количество потоков больше, чем количество реальных ядер в системе. - И плохо для любых других случаев - вы должны использовать отдельный фиксированный пул потоков (и, возможно, опрос).
P.S. blocking
скрыт внутри Await.result
, так что это не всегда очевидно. В нашем проекте кто-то только что сделал Await
внутри какого-то основного рабочего актера.
Кстати, может быть, вы знаете любые другие блокирующие реализации, чем 'ForkJoinPool'? Я не нашел ... – dk14
Не то, чтобы я видел. –