Scala未来是否会阻止长期操作?

时间:2015-02-17 05:54:06

标签: playframework-2.0 akka future spray executioncontext

我们可以在任何地方读到,在执行长时间运行操作或阻塞操作时,最好使用特殊的执行上下文。阻止访问数据库等操作。我理解为什么。这是为了避免线程饥饿。我们不希望“8”可用线程忙于一些可能最终返回或阻塞的阻塞代码。它要么严重减慢应用程序的速度,要么无限期地阻止它。

与此同时,我想知道如何实施Spray或Play之类的东西。实际上,让我们采取客户端。发送请求时,我们会得到未来的响应。换句话说,请求是异步执行的。这可能最终成为一个长期运行的操作。但是,没有任何内容表明在这种情况下启动许多请求可能会导致线程不足。因此,我想知道为什么在这种情况下它不是一个问题。他们有特殊的线程池吗?

我在“在Scala中学习并发编程”一书中使用“未来中的阻塞{}”语句块帮助其调度程序自动生成更多线程。这可能是他们处理它的方式吗?

接收请求可以说同样的事情,在游戏中我们可以执行异步操作。如果想要从此操作访问数据库,则应使用“阻塞{}”语句块。如何执行该操作是一个特殊的threadPool / ExecutionContext。

  

我的假设是他们依赖于implicit.global   执行上下文。也许我错了。底线是。提出要求   默认是一个很长的操作,如何在你的使用喷雾   代码,将处理它,以便不创建线程饥饿   你的代码?

我们使用不同的ExecutionContext吗?

编辑:刚发现这个简短的演示文稿Don't Block - How to Mess Up Akka and Spray恰好可以更好地说明我在这里遇到的问题。

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

编辑:以下是我在使用未来时以某种方式发生的事情:

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) }
  }
} } 

取自:Clarification needed about futures and promises in Scala 我在“学习Scala中的并发编程”一书中更简单和类似的东西

  

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

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:1)

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

通过使用epoll / kqueue,一个线程可以等待许多I / O事件同时,如果没有I / O响应,这个线程正在等待(饿死),但是你只看到这个线程正在等待。

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