Использовать контексты исполнения vs scala global
Как контекст выполнения из
import scala.concurrent.ExecutionContext.Implicits.global
отличаются от контекстов исполнения воспроизведения:
import play.core.Execution.Implicits.{internalContext, defaultContext}
Ответы
Ответ 1
Они очень разные.
В Play 2.3.x и предыдущем, play.core.Execution.Implicits.internalContext
является ForkJoinPool
с фиксированными ограничениями на размер, которые используются внутри игры. Вы никогда не должны использовать его для своего кода приложения. Из документов:
Воспроизведение внутреннего пула потоков - это используется внутри игры. Никакой код приложения никогда не должен выполняться потоком в этом пуле потоков, и в этом пуле потоков никогда не должно быть блокировки. Его размер можно настроить, установив параметр internal-threadpool-size в application.conf, и по умолчанию используется количество доступных процессоров.
Вместо этого вы должны использовать play.api.libs.concurrent.Execution.Implicits.defaultContext
, который использует ActorSystem
.
В 2.4.x оба они используют один и тот же ActorSystem
. Это означает, что Akka будет распространять работу среди своего пула потоков, но таким образом, который невидим для вас (кроме конфигурации). Несколько актеров Akka могут делиться одним и тем же потоком.
scala.concurrent.ExecutionContext.Implicits.global
является ExecutionContext
, определенным в стандартной библиотеке Scala. Это специальный ForkJoinPool
, который использует метод blocking
для обработки потенциально блокирующего кода, чтобы порождать новые потоки в пуле. Вы действительно не должны использовать это в приложении Play, так как Play не будет контролировать его. Он также имеет потенциал для создания множества потоков и использования тонны памяти, если вы не будете осторожны.
Я написал больше о scala.concurrent.ExecutionContext.Implicits.global
в этом ответе.
Ответ 2
Они одинаковы и указывают на диспетчера по умолчанию базовой действующей системы в вашем
Play или Akka или комбинированное приложение.
Контекст воспроизведения по умолчанию
play.api.libs.concurrent.Execution.Implicits.defaultContext
Воспроизвести внутренний контекст
play.core.Execution.Implicits.internalContext
Гик-код EC Injected
class ClassA @Inject()(config: Configuration)
(implicit ec: ExecutionContext) {
...
}
Но это другое:
scala.concurrent.ExecutionContext.Implicits.global
Также драйверы DB, например. если вы используете пятно, может возникнуть свой собственный контекст выполнения. Во всяком случае,
Рекомендации:
- Не используйте
scala.concurrent.ExecutionContext.Implicits.global
, когда вы находитесь в игре или в каркасе akka, таким образом вы можете использовать больше потоков, чем оптимально во время высокой нагрузки, поэтому производительность может уменьшиться.
- Не бойтесь! используйте диспетчер по умолчанию столько, сколько вы хотите повсюду, если вы не выполняете какую-либо задачу блокировки, например, при прослушивании в сетевом соединении, или явно читаете из db, что делает вас "текущей тидрой", ожидающей результата.
- Начните с исполнителя по умолчанию, и если вы обнаружили, что Play/Akka плохо реагирует во время высокой нагрузки, переключитесь на новый пул потоков для трудоемких задач вычисления.
- Вычислительные задачи, которые занимают много времени, обычно не рассматриваются как блокирование. Например, перемещение дерева автозаполнения в памяти. Но вы можете считать их блокирующими, если хотите, чтобы ваши управляющие структуры оставались работоспособными, когда у вас есть время, затрачиваемое на выполнение вычислительной задачи.
- Плохая вещь, которая может случиться, когда вы считаете, что задачи вычисления не блокируются, заключается в том, что диспетчер сообщений игры и Akka будет приостановлен, когда все потоки будут вычисляться с большой нагрузкой. Преимуществом отдельного диспетчера является то, что процессор очереди не голодает. The Cons с отдельным диспетчером состоит в том, что вы можете выделить больше потоков, которые будут оптимальными, и ваша общая производительность будет уменьшена.
- Разница заключается в серверах с высокой нагрузкой, не беспокоитесь о небольших проектах, используйте по умолчанию
- Используйте
scala.concurrent.ExecutionContext.Implicits.global
, если в вашем приложении нет другого исполнителя. Не беспокойтесь, это безопасно тогда.
- После создания фьючерсов используйте пул по умолчанию, это самый безопасный способ, если вы не уверены, что будущее блокирует. Затем используйте отдельный пул или используйте блокировку {}, если это возможно.
- Создайте отдельный пул потоков один раз
- Вы
Await
для будущего
- Вы вызываете Thread.sleep
- Вы читаете вызов stream/socket/http
- Вручную запрашивать db с помощью драйвера блокировки (обычно это безопасно)
- Запланировать задачу, которая будет запущена за 10 секунд
- Запланировать задачу, которая будет выполняться каждую секунду
- Для операций отображения/восстановления в будущем используйте исполнитель по умолчанию, обычно это безопасно
- Обработка исключений безопасна с диспетчером по умолчанию
- Всегда используйте диспетчеров Akka с вами в Play или Akka, имеет хороший способ определить нового диспетчера в
application.conf