我们到处都可以读到,在执行长时间运行的操作或阻塞操作时,最好使用特殊的执行上下文来解决这个问题。像访问数据库这样的阻塞操作。我明白为什么。这是为了避免线程饥饿。我们不希望“8”个可用线程忙于一些可能最终返回或继续阻塞的阻塞代码。它要么严重减慢应用程序的速度,要么无限期地阻止它。
同时,我想知道像 Spray 或 Play 这样的东西是如何实现的。确实,让我们以客户端为例。当请求被发送时,我们会得到一个未来的响应。换句话说,请求是异步执行的。顺便说一句,这可能最终成为一项长期运行的操作。但是,在这种情况下,没有什么可以说发起许多请求会导致线程饥饿。因此,我想知道为什么在这种情况下它不是问题。他们有特殊的线程池吗?
我在“Learning concurrent programming in Scala”一书中写道,在 Future 中使用“Blocking {}”语句块有助于其调度程序自动生成更多线程。会不会是他们处理的方式?
接收请求也可以这样说,在游戏中我们可以执行异步操作。如果想从此操作访问数据库,应使用“Blocking {}”语句块。如何执行那个动作是一个特殊的threadPool/ExecutionContext。
我在这里的假设是它们依赖于implicit.global ExecutionContext。也许我错了。底线是。默认情况下发出请求是一个很长的操作,例如在您的代码中使用喷雾如何处理它以便不在您的代码中创建线程饥饿?
我们使用不同的 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) }
}
} }
摘自:需要澄清 Scala 中的 future 和 promises 我在“Learning concurrent programming in Scala”一书中写了一些更简单和类似的东西
这对我来说意味着:线程池中有一个线程,它使该任务出列并尝试使用该任务的执行结果设置一个承诺未来值。如果这是正确的,我看不出进行 IO 调用的任务如何不会阻止该线程的运行。