Playframework 2.0 Scala future是否会阻止长时间操作?

Playframework 2.0 Scala future是否会阻止长时间操作?,playframework-2.0,akka,future,spray,executioncontext,Playframework 2.0,Akka,Future,Spray,Executioncontext,在任何地方我们都可以看到,当执行长时间运行的操作或阻塞操作时,最好使用特殊的执行上下文。阻止访问数据库等操作。我明白为什么。这是为了避免线程不足。我们不想让“8”个可用线程忙于某些阻塞代码,这些代码最终可能返回或继续阻塞。它要么严重减慢应用程序的速度,要么无限期地阻止它 同时,我想知道像Spray或Play这样的东西是如何实现的。事实上,让我们看看客户端。当一个请求被发送时,我们会得到一个未来的响应。换句话说,请求是异步执行的。顺便说一句,这可能是一个长期运行的操作。然而,在这种情况下,并没有任

在任何地方我们都可以看到,当执行长时间运行的操作或阻塞操作时,最好使用特殊的执行上下文。阻止访问数据库等操作。我明白为什么。这是为了避免线程不足。我们不想让“8”个可用线程忙于某些阻塞代码,这些代码最终可能返回或继续阻塞。它要么严重减慢应用程序的速度,要么无限期地阻止它

同时,我想知道像Spray或Play这样的东西是如何实现的。事实上,让我们看看客户端。当一个请求被发送时,我们会得到一个未来的响应。换句话说,请求是异步执行的。顺便说一句,这可能是一个长期运行的操作。然而,在这种情况下,并没有任何证据表明启动多个请求会导致线程不足。因此,我想知道为什么在这种情况下,这不是一个问题。他们有专门的线程池吗

我在《学习Scala中的并发编程》一书中提到,将来使用“Blocking{}”语句块有助于其调度器自动生成更多线程。这可能是他们处理此事的方式吗

对于接收请求也可以这样说,在游戏中我们可以执行异步操作。如果希望通过此操作访问数据库,应使用“Blocking{}”语句块。如何执行该操作是一个特殊的threadPool/ExecutionContext

我在这里的假设是,它们依赖于隐式.global 执行上下文。也许我错了。底线是。提出请求 默认情况下是一个较长的操作,例如如何在您的 代码,将对其进行处理,以避免在 你的密码

我们使用的是不同的ExecutionContext吗

编辑:刚刚看到这个简短的演示,正好更好地说明了我在这里遇到的问题

无论如何,我希望有其他意见

编辑:以下是我所了解到的,在使用未来时会发生的事情:

def apply[T](body: =>T): Future[T] = impl.Future(body)  //here I have omitted the implicit ExecutorContext
impl.Future is an implementation of Future trait:

def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] =
{
  val runnable = new PromiseCompletingRunnable(body)
  executor.prepare.execute(runnable)
  runnable.promise.future
}
PromiseCompletingRunnable如下所示:

class PromiseCompletingRunnable[T](body: => T) extends Runnable {
val promise = new Promise.DefaultPromise[T]()

override def run() = {
  promise complete {
    try Success(body) catch { case NonFatal(e) => Failure(e) }
  }
} } 
摘自: 我在《在Scala中学习并发编程》一书中提到了一些更简单和类似的东西

对我来说,这意味着:线程池中有一个线程,它将该任务出列 并尝试设定一个承诺未来价值 执行该任务的结果。如果这是正确的,我看不出发出IO调用的任务如何不阻止该线程的运行


我认为您不理解的是,当您使用阻塞API发出客户端请求时,线程会阻塞,坐在那里什么也不做,直到响应返回。但是,如果使用异步API,在等待响应返回时,没有线程等待。当它返回时,当然会从执行上下文中拉出一个线程来完成工作,但这正是您希望线程完成的工作——工作,而不是什么都不做。让数百个线程无所事事地等待客户机请求或数据库查询返回是一种资源浪费。因为它们不是免费的,你必须限制它们,这就是线程饥饿的原因。在异步框架中,只有在有工作要做时才使用线程。这意味着,如果每个CPU核心有一个线程,如果耗尽线程池,则意味着CPU的利用率为100%,而在阻塞框架中,线程池的利用率仅为10%。请记住,大多数普通web应用程序的大部分时间都花在IO上,即等待数据库调用或http客户端调用返回。等待的数量与实际工作的数量通常是一个数量级或更大的数量级。因此,只有在有工作要做时才使用线程是一个很大的优势。

我认为您不理解的是,当您使用阻塞API发出客户机请求时,线程会阻塞,坐在那里什么也不做,直到响应返回。但是,如果使用异步API,在等待响应返回时,没有线程等待。当它返回时,当然会从执行上下文中拉出一个线程来完成工作,但这正是您希望线程完成的工作——工作,而不是什么都不做。让数百个线程无所事事地等待客户机请求或数据库查询返回是一种资源浪费。因为它们不是免费的,你必须限制它们,这就是线程饥饿的原因。在异步框架中,只有在有工作要做时才使用线程。这意味着,如果每个CPU核心有一个线程,如果耗尽线程池,则意味着CPU的利用率为100%,而在阻塞框架中,线程池的利用率仅为10%。请记住,大多数普通web应用程序的大部分时间都花在IO上,即等待数据库调用或http客户端调用返回。等待的数量与实际工作的数量通常是一个数量级或更大的数量级。因此,只有在有工作要做时才使用线程是一个很大的优势。

以I/O操作为例,我认为唯一缺少的环节是I/O多路复用,它可以通过epoll或kqueue实现

通过使用epoll/kqueue,一个线程可以同时等待多个I/O事件,如果没有I/O响应,则该线程正在等待(饥饿),但您只能看到该线程正在等待


nginx和nodej都使用这种工作模式。

以I/O操作为例,我认为唯一缺少的链接是I/O多路复用,它可以通过epoll或kqueue实现

通过使用epoll/kqueue,一个线程可以同时等待多个I/O事件,如果没有I/O响应,则该线程正在等待(饥饿),但您只能看到该线程正在等待


nginx和nodej都在使用这种工作模式。

首先,非常感谢您提供的详细答案。然而我想,我明白了