首页 文章

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

提问于
浏览
1

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

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

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

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

我的假设是它们依赖于implicit.global ExecutionContext . 也许我错了 . 底线是 . 默认情况下,发出请求是一个很长的操作,如何在代码中使用spray来处理它,以便不在代码中创建Thread Starvation?

我们使用不同的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我在书中更简单和类似的东西"Learning concurrent programing in Scala"

这对我来说意味着:ThreadPool中有一个Thread,它使该任务出列,并尝试使用该任务的执行结果设置promise future值 . 如果这是正确的,我不知道进行IO调用的任务如何不会阻止该线程的运行 .

2 回答

  • 4

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

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

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

  • 1

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

相关问题