どこでも、長時間実行操作またはブロック操作を実行する場合は、特別な実行コンテキストを使用することをお勧めします。データベースへのアクセスなどのブロック操作。理由はわかりました。スレッドの枯渇を避けるためです。「8」個の使用可能なスレッドが、最終的に返されるか、ブロックし続ける可能性のあるブロック コードでビジー状態になることは望ましくありません。アプリケーションの速度が大幅に低下するか、無期限にブロックされます。
一方、Spray や Play などはどのように実装されているのか気になります。実際、クライアント側を考えてみましょう。リクエストが送信されると、将来のレスポンスが返されます。つまり、リクエストは非同期で実行されます。ところで、これは長時間実行される操作になる可能性があります。ただし、これらのケースでは、多くのリクエストを起動するとスレッドが枯渇する可能性があるとは言えません。したがって、なぜその場合は問題にならないのか疑問に思っています。彼らは特別なスレッドプールを持っていますか?
本「Scala での並行プログラミングの学習」で、Future で「ブロッキング {}」ステートメント ブロックを使用すると、スケジューラがより多くのスレッドを自動的に生成するのに役立つことを強調しました。それは彼らがそれを処理する方法でしょうか?
リクエストの受信についても同じことが言えます。実際に非同期アクションを実行します。このアクションからデータベースにアクセスする場合は、「ブロッキング {}」ステートメント ブロックを使用する必要があります。そのアクションを実行する方法は、特別な 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 でのフューチャーとプロミスについて必要な明確化 私は、「Scala で並行プログラミングを学ぶ」という本で、より単純で類似したものを赤くしました。
これは、ThreadPool に Thread があり、そのタスクをデキューし、そのタスクの実行結果で promise の将来の値を設定しようとすることを意味します。それが正しければ、IO呼び出しを行うタスクがそのスレッドの実行をブロックしない方法がわかりません。