1

どこでも、長時間実行操作またはブロック操作を実行する場合は、特別な実行コンテキストを使用することをお勧めします。データベースへのアクセスなどのブロック操作。理由はわかりました。スレッドの枯渇を避けるためです。「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呼び出しを行うタスクがそのスレッドの実行をブロックしない方法がわかりません。

4

2 に答える 2

5

あなたが理解していないのは、ブロッキングAPIを使用してクライアントリクエストを行うと、スレッドがブロックされ、応答が返されるまで何もしないということです。ただし、非同期 API を使用すると、応答が戻ってくるのを待っている間、待機しているスレッドはありません。それが戻ってくると、確かに、スレッドは実行コンテキストから引き出されて作業を行いますが、それはスレッドに実行させたいことです-何もしないのではなく、作業してください. 何百ものスレッドが何もせずにクライアントの要求やデータベース クエリが返されるのを待っていると、リソースが無駄になります。そして、それらは無料ではないため、それらを制限する必要があり、そこでスレッド不足が発生します。非同期フレームワークでは、スレッドはやるべき作業がある場合にのみ使用されます。つまり、CPU コアごとに 1 つのスレッドがある場合、スレッド プールを使い果たした場合、CPU が 100% 使用されていることを意味しますが、ブロッキング フレームワークでは、10% の CPU 使用率でスレッド プールを使い果たす可能性があります。ほとんどの平均的な Web アプリは、ほとんどの時間を IO に費やしています。つまり、データベース呼び出しまたは HTTP クライアント呼び出しが返されるのを待っています。多くの場合、待機時間と実際の作業量は、1 桁以上大きくなります。したがって、実行する作業がある場合にのみスレッドを使用することは大きな利点です。

于 2015-02-17T10:15:47.353 に答える
1

例として I/O 操作を取り上げます。欠落している唯一のリンクは、epoll または kqueue で実装できる I/O 多重化だと思います。

epoll/kqueue を使用すると、1 つのスレッドが多数の I/O イベントを同時に待機できます。I /O 応答がない場合、このスレッドは待機 (飢餓) しますが、このスレッドだけが待機していることがわかります。

nginx と nodejs の両方がこの作業モードを使用しています。

于 2016-07-15T13:40:02.197 に答える