2015-04-16 3 views
4

Что касается третьего пункта в принятом ответе this, существуют ли случаи, для которых было бы бессмысленно или плохо использовать blocking для долговременного вычисления, будь то ЦПУ или IO-связанный, который выполняется «внутри», a Future?Плохие примеры использования scala.concurrent.blocking?

ответ

4

Это зависит от того, ExecutionContext ваш Future в настоящее время выполняется в

Pointless:.

Если ExecutionContext не BlockContext, то с помощью blocking будет бессмысленно. То есть, он будет использовать DefaultBlockContext, который просто выполняет код без какой-либо специальной обработки. Вероятно, это не добавило бы слишком много накладных расходов, но тем не менее бессмысленно.

Bad:

в Scala ExecutionContext.Implicits.global сделан на нерест новые потоки в ForkJoinPool когда пул потоков собирается быть исчерпаны. То есть, если знает, что должно произойти через blocking. Это может быть плохо, если вы нерест лотов тем. Если в течение короткого промежутка времени вы заработаете много работы, контекст global будет радостно расширяться до тупика. Ответ @ dk14 объясняет это более подробно, но суть в том, что это может быть убийца производительности, поскольку управляемая блокировка может стать быстро неуправляемой.


Основная цель blocking, чтобы избежать тупиков внутри пулов потоков, так что косвенное отношение к производительности в том смысле, что достижение тупика будет хуже, чем нерест несколько больше нитей. Тем не менее, это определенно не волшебный усилитель производительности.

Я писал больше о blocking, в частности, в this answer.

+0

Кстати, может быть, вы знаете любые другие блокирующие реализации, чем 'ForkJoinPool'? Я не нашел ... – dk14

+0

Не то, чтобы я видел. –

4

From my practice, blocking + ForkJoinPool может привести к contionuous и неконтролируемому созданию потоков, если у вас есть много сообщений, чтобы обработать и каждый из них требует длительной блокировки (что также означает, что она имеет некоторую память во время таких). ForkJoinPool создает новый поток для компенсации «управляемой блокировки», независимо от MaxThreadCount; приветствуйте сотни потоков в VisualVm. И это почти убивает противодавление, поскольку в очереди пула всегда есть место для задачи (если ваше противодавление основано на политике ThreadPoolExecutor). Производительность становится убиваемой как при сборе новых потоков, так и в сборке мусора.

Итак:

  • это хорошо, когда скорость сообщения не намного выше, чем 1/blocking_time, поскольку это позволяет использовать полную мощность потоков. Некоторое интеллектуальное противодавление может помочь замедлить входящие сообщения.
  • Бессмысленно, если задача фактически использует ваш процессор во время blocking{} (без блокировок), так как он просто увеличит количество потоков больше, чем количество реальных ядер в системе.
  • И плохо для любых других случаев - вы должны использовать отдельный фиксированный пул потоков (и, возможно, опрос).

P.S. blocking скрыт внутри Await.result, так что это не всегда очевидно. В нашем проекте кто-то только что сделал Await внутри какого-то основного рабочего актера.