Play的执行上下文与Scala全局上下文的区别

35

执行上下文是什么?

import scala.concurrent.ExecutionContext.Implicits.global

与 Play 的执行上下文不同:

import play.core.Execution.Implicits.{internalContext, defaultContext}
3个回答

48
它们非常不同。
在Play 2.3.x及更早版本中,play.core.Execution.Implicits.internalContext是一个具有固定大小限制的ForkJoinPool,由Play内部使用。您永远不应在应用程序代码中使用它。从文档中得知:
  

Play内部线程池 - 这是由Play内部使用的。没有应用程序代码应该由此线程池中的线程执行,并且在此线程池中永远不应阻止。它的大小可以通过在application.conf中设置internal-threadpool-size来配置,默认为可用处理器数量。

相反,您将使用play.api.libs.concurrent.Execution.Implicits.defaultContext,它使用ActorSystem
在2.4.x中,它们都使用相同的ActorSystem。这意味着Akka将在其自己的线程池中分配工作,但以对您不可见的方式(除了配置)。几个Akka actors可以共享同一个线程。 scala.concurrent.ExecutionContext.Implicits.global是在Scala标准库中定义的ExecutionContext。它是一个特殊的ForkJoinPool,使用blocking方法处理可能阻塞的代码,以便在池中生成新线程。您真的不应在Play应用程序中使用它,因为Play无法控制它。如果不小心,它还有可能产生大量线程并使用大量内存。
我在此答案中更详细地描述了scala.concurrent.ExecutionContext.Implicits.global

13

它们是相同的,指向您的Play或Akka或组合应用程序底层actor系统的默认调度程序。

## Play默认上下文

play.api.libs.concurrent.Execution.Implicits.defaultContext

##Play的内部环境

play.core.Execution.Implicits.internalContext

##Guice的EC注入

class ClassA @Inject()(config: Configuration)
                           (implicit ec: ExecutionContext) {
...
}

但这是不同的:

scala.concurrent.ExecutionContext.Implicits.global

此外,例如如果您使用slick,数据库驱动程序可能会提供自己的Execution Context。无论如何,


最佳实践:

  • 当您使用Play或Akka框架时,请不要使用scala.concurrent.ExecutionContext.Implicits.global,否则在高负载期间可能会使用比最优更多的线程,从而导致性能降低。
  • 不要害怕!除非进行某些阻塞任务(例如监听网络连接或显式从数据库读取),否则可以在任何地方尽可能使用默认调度程序。
  • 首先使用默认执行器,如果在高负载期间发现Play/Akka响应不好,请切换到新的线程池以进行耗时的计算任务。
    • 通常不认为计算任务需要很长时间就会导致阻塞。例如,在内存中遍历自动完成树的计算任务。但是,当您希望控制结构在执行耗时的计算任务后保持有效时,您可能会认为它们是阻塞的。
    • 将计算任务视为非阻塞时可能发生的坏事情是,当所有线程都处于重载状态时,Play和Akka消息调度程序将暂停。单独调度程序的优点是队列处理器不会饿死。单独调度程序的缺点是您可能会分配比最优更多的线程,从而降低整体性能。
    • 区别在于高负载服务器,对于小型项目不用担心,请使用默认值
  • 当您的应用程序中没有其他执行器正在运行时,请使用scala.concurrent.ExecutionContext.Implicits.global。这是安全的。
  • 一旦创建了Futures,请使用默认池,这是最安全的方式,除非您确定该future是阻塞的。然后请使用单独的池或尽可能使用blocking{}结构。
  • 在以下情况下创建一个单独的线程池
    • 您正在等待未来
    • 您调用了Thread.sleep
    • 您正在读取流/套接字/http调用
    • 使用阻塞驱动程序手动查询数据库(通常是slick是安全的)
    • 计划在10秒内运行任务
    • 计划每秒运行任务
  • 对于Future的map/recover操作,请使用默认执行器,通常这是安全的
  • 默认调度程序下的异常处理是安全的
  • 始终在Play或Akka中使用Akka调度程序,在application.conf中定义新调度程序的方法很好用

1

前言: 此问题来自6年前,自那时起很多事情已经改变。我知道这不是对原始问题的回答,但我曾受到与原始问题陈述相同的困惑超过1天;所以我决定与社区分享我的研究结果。

最新的关于ExecutionContext的更新,完美适用于Play 2.8.15, 如下所示。 Play 2.6迁移指南

play.api.libs.concurrent.Execution类已弃用,因为它在内部使用全局可变状态来获取“当前”应用程序的ExecutionContext。 如果您想指定之前隐式行为,则应在构造函数中传递execution context。

因此,您不能再使用play.api.libs.concurrent.Execution.Implicits.defaultContext。无需配置即可使用的做法是为控制器提供一个类型为scala.concurrent.ExecutionContext的隐式值,例如:

import scala.concurrent.ExecutionContext

@Singleton
class AsyncController @Inject()(cc: ControllerComponents, actorSystem: ActorSystem)(implicit exec: ExecutionContext) extends AbstractController(cc)

这意味着以上答案都不适用了,而且问题本身也已经不相关了,因为play.core.Execution.Implicits.defaultContext不再可用。


整洁,谢谢!我已经有5年没用Play了,但这篇文章很有趣,也很有用。 \o/ - bjfletcher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接