ノードにはまったく異なるパラダイムがあり、正しくキャプチャされると、問題を解決するこの異なる方法を簡単に確認できます。同じことを別の方法で行うため、Node アプリケーション (1) で複数のスレッドが必要になることはありません。複数のプロセスを作成します。しかし、たとえば Apache Web Server の Prefork mpm とは大きく異なります。
今のところ、CPU コアが 1 つしかないと考えて、何らかの作業を行うアプリケーションを (ノードの方法で) 開発します。私たちの仕事は、その内容をバイト単位で実行する大きなファイルを処理することです。私たちのソフトウェアにとって最善の方法は、ファイルの先頭から作業を開始し、バイト単位で最後まで作業を進めることです。
-- ヘイ、ハサン、あなたは私の祖父の時代からの初心者か非常に古い学校のどちらかだと思います!!! いくつかのスレッドを作成して、はるかに高速にしてみませんか?
-- あ、CPU コアが 1 つしかありません。
- だから何?いくつかのスレッドを作成して、高速化してください!
――そうはいきません。スレッドを作成すると、スレッドが遅くなります。スレッド間の切り替えのためにシステムに多くのオーバーヘッドを追加するため、スレッドに適切な時間を与えようとし、プロセス内でこれらのスレッド間の通信を試みます。これらすべての事実に加えて、1 つのジョブを並行して実行できる複数の部分に分割する方法についても考える必要があります。
-- わかりました、わかりました、あなたは貧乏です。私のコンピューターを使ってみましょう。32 コアを搭載しています。
-- うわー、あなたは素晴らしい私の親愛なる友人です、どうもありがとう。それは有り難いです!
それから私たちは仕事に戻ります。私たちの豊かな友人のおかげで、今では 32 個の CPU コアがあります。私たちが守らなければならないルールが変わったばかりです。今、私たちは与えられたすべての富を活用したいと考えています。
複数のコアを使用するには、作業を分割して並列処理できるようにする方法を見つける必要があります。ノードでない場合は、これにスレッドを使用します。各 CPU コアに 1 つずつ、32 スレッド。ただし、Node があるため、32 個の Node プロセスを作成します。
スレッドは、Node プロセスの優れた代替手段になる可能性があります。ただし、作業がすでに定義されており、その処理方法を完全に制御できる特定の種類のジョブのみです。これ以外に、私たちが制御できない方法で仕事が外部からもたらされ、できるだけ早く答えたいと考えている他のすべての種類の問題については、ノードの方法が間違いなく優れています。
-- ハサンさん、まだシングル スレッドで作業していますか? 何が悪いの?私はちょうどあなたが望んでいたものを提供しました。もう言い訳はありません。スレッドを作成し、より高速に実行します。
-- 作業をいくつかの部分に分割しました。すべてのプロセスは、これらの部分の 1 つに対して並行して実行されます。
――スレッドを立ててみませんか?
-- すいません、使えないと思います。必要に応じて、コンピューターを使用できますか?
-- いいえ、私はクールです。なぜあなたがスレッドを使用しないのか理解できませんか?
-- パソコンありがとうございます。:) 私はすでに作業をいくつかの部分に分割しており、これらの部分を並行して処理するプロセスを作成しています。すべての CPU コアが完全に使用されます。プロセスではなくスレッドでこれを行うことができました。しかし、Nodeにはこの方法があり、上司のParth ThakkarはNodeを使用することを望んでいます。
-- わかりました、別のコンピューターが必要な場合はお知らせください。:p
32 ではなく 33 のプロセスを作成すると、オペレーティング システムのスケジューラはスレッドを一時停止し、別のスレッドを開始し、数サイクル後に一時停止し、別のスレッドを再度開始します...これは不要なオーバーヘッドです。いりません。実際、32 個のコアを持つシステムでは、正確に 32 個のプロセスを作成したくはありません。31個の方が適切です。このシステムで動作するのは私のアプリケーションだけではないからです。特に部屋が 32 室ある場合は、他のもののために少しスペースを空けておくとよいでしょう。
CPU を集中的に使用するタスクにプロセッサを最大限に活用することについて、私たちは今、同じページにいると思います。
――うーん、ハサンさん、ちょっとからかってすみません。私は今あなたをよりよく理解していると信じています。しかし、まだ説明が必要なことがあります。何百ものスレッドを実行することについて話題になっているのは何ですか? プロセスをフォークするよりもスレッドの作成がはるかに高速で、愚かであることをどこでも読みましたか? スレッドの代わりにプロセスを fork し、Node.js で得られる最高のものだと思います。では、Nodeはこの種の作業には適していませんか?
――ご安心ください、私もかっこいいです。こういうことはみんな言うので、聞き慣れていると思います。
- そう?ノードはこれには適していませんか?
-- スレッドも良いかもしれませんが、ノードはこれに完全に適しています。スレッド/プロセス作成のオーバーヘッドについては; 何度も繰り返すことでは、1 ミリ秒も重要ではありません。ただし、作成するプロセスは 32 個しかなく、わずかな時間がかかります。それは一度だけ起こります。違いはありません。
-- では、何千ものスレッドを作成したいのはいつですか?
-- 何千ものスレッドを作成したくありません。ただし、HTTP 要求を処理する Web サーバーのように、外部からの作業を行っているシステムでは、リクエストごとにスレッドを使用している場合は、多くのスレッドを作成することになります。
――ノードが違うんですけど?右?
- はい、正確に。これが Node の真価を発揮するところです。スレッドがプロセスよりもはるかに軽量であるように、関数呼び出しはスレッドよりもはるかに軽量です。ノードは、スレッドを作成する代わりに関数を呼び出します。Web サーバーの例では、着信要求ごとに関数呼び出しが発生します。
-- うーん、興味深いです。ただし、複数のスレッドを使用していない場合は、同時に 1 つの関数しか実行できません。多数のリクエストが同時に Web サーバーに到着した場合、これはどのように機能しますか?
-- 関数が一度に 1 つずつ実行され、2 つの関数が同時に実行されることはありません。つまり、1 つのプロセスでは、一度に実行されるコードのスコープは 1 つだけです。OS スケジューラは、プロセス内の別のスレッドではなく、別のプロセスに時間を与えるためにプロセスを一時停止しない限り、この関数を一時停止して別の関数に切り替えることはありません。(2)
-- それでは、プロセスが一度に 2 つのリクエストを処理するにはどうすればよいでしょうか。
-- システムに十分なリソース (RAM、ネットワークなど) がある限り、プロセスは一度に数万のリクエストを処理できます。これらの機能がどのように実行されるかが重要な違いです。
――うーん、今ドキドキしていいですか?
-- たぶん :) ノードはキューをループします。このキューにはジョブ、つまり着信リクエストを処理するために開始した呼び出しがあります。ここで最も重要な点は、関数を実行するように設計する方法です。リクエストの処理を開始し、ジョブが完了するまで呼び出し元を待たせる代わりに、許容できる量の作業を行った後、関数をすぐに終了します。別のコンポーネントが何らかの作業を行って値を返すのを待つ必要がある場合、それを待つ代わりに、残りの作業をキューに追加して関数を終了するだけです。
--複雑すぎませんか?
-- いいえ、複雑に聞こえるかもしれません。しかし、システム自体は非常にシンプルで、理にかなっています。
ここで、この 2 人の開発者の間の対話を引用するのをやめ、これらの関数がどのように機能するかを示す最後の簡単な例を示した後で、回答を締めくくりたいと思います。
このようにして、OS スケジューラが通常行うことを行っています。ある時点で作業を一時停止し、別の関数呼び出し (マルチスレッド環境の他のスレッドなど) を次の順番になるまで実行させます。これは、システム上のすべてのスレッドに時間を与えようとする OS スケジューラに作業を任せるよりもはるかに優れています。私たちは、OS スケジューラよりもはるかによく何をしているかを知っており、停止すべきときに停止することが期待されています。
以下は、ファイルを開いて読み取り、データに対して何らかの作業を行う簡単な例です。
同期方法:
Open File
Repeat This:
Read Some
Do the work
非同期の方法:
Open File and Do this when it is ready: // Our function returns
Repeat this:
Read Some and when it is ready: // Returns again
Do some work
ご覧のとおり、この関数はシステムにファイルを開くように要求し、ファイルが開かれるのを待ちません。ファイルの準備ができたら、次のステップを提供することで終了します。戻ると、Node はキューで他の関数呼び出しを実行します。すべての関数を実行した後、イベント ループは次のターンに移動します...
要約すると、Node にはマルチスレッド開発とはまったく異なるパラダイムがあります。しかし、これは物事が欠けているという意味ではありません。同期ジョブ (処理の順序と方法を決定できる場合) の場合、マルチスレッド並列処理と同様に機能します。サーバーへのリクエストのように外部から来るジョブの場合、それは単に優れています。
(1) C/C++ などの他の言語でライブラリを構築している場合を除きます。その場合でも、ジョブを分割するためのスレッドは作成しません。この種の作業では、2 つのスレッドがあり、そのうちの 1 つは Node との通信を継続し、もう 1 つは実際の作業を行います。
(2) 実際、最初の脚注で述べたのと同じ理由で、すべての Node プロセスには複数のスレッドがあります。ただし、これは 1000 のスレッドが同様の作業を行うようなものではありません。これらの余分なスレッドは、IO イベントを受け入れたり、プロセス間メッセージングを処理したりするためのものです。
更新 (コメントの良い質問への返信として)
@マーク、建設的な批判をありがとう。Node のパラダイムでは、キュー内の他のすべての呼び出しが次々に実行されるように設計されていない限り、処理に時間がかかりすぎる関数を使用するべきではありません。計算コストの高いタスクの場合、全体像を見ると、これは「スレッドまたはプロセスを使用する必要があるか」という問題ではないことがわかります。しかし、「これらのタスクをバランスよくサブタスクに分割し、システム上で複数の CPU コアを使用して並行して実行するにはどうすればよいか」という問題があります。8 コアのシステムで 400 個のビデオ ファイルを処理するとします。一度に 1 つのファイルを処理する場合は、同じファイルのさまざまな部分を処理するシステムが必要です。その場合、おそらく、マルチスレッドの単一プロセス システムを構築する方が簡単で、さらに効率的です。状態共有/通信が必要な場合は、複数のプロセスを実行し、それらの間でメッセージを渡すことにより、これに Node を引き続き使用できます。前に述べたように、Node を使用したマルチプロセス アプローチは、この種のタスクでのマルチスレッド アプローチと同様に。しかし、それ以上ではありません。繰り返しますが、前に述べたように、ノードが輝く状況は、これらのタスクが複数のソースからシステムへの入力として来る場合です。ノードでは、接続ごとのスレッドまたは接続ごとのプロセスと比較して、多くの接続を同時に維持する方がはるかに軽いからです。システム。
通話に関してsetTimeout(...,0)
は; 時間のかかるタスク中に休憩を与えて、キュー内の呼び出しが処理を共有できるようにすることが必要になる場合があります。タスクをさまざまな方法で分割することで、これらの問題を回避できます。ただし、これは実際にはハックではなく、イベント キューが機能する方法です。また、process.nextTick
この目的で を使用することは、 を使用するsetTimeout
ときに経過時間の計算とチェックが必要になるため、はるかに優れprocess.nextTick
ています。 "