web-dev-qa-db-ja.com

node.jsの同時タスクにはどちらが良いでしょうか?繊維?ウェブワーカー?またはスレッド?

しばらく前にnode.jsを見つけましたが、とても気に入っています。しかし、すぐに、CPU集中型のタスクを実行する能力がひどく不足していることがわかりました。そこで、私はグーグル検索を開始し、問題を解決するためにこれらの回答を得ました:ファイバー、ウェブワーカー、スレッド(thread-a-gogo)。どちらを使用するかは混乱であり、そのうちの1つを使用する必要があります。結局のところ、IOだけが得意なサーバーを持つ目的は何でしょうか。提案が必要です!

更新:

私は遅れている方法を考えていました。それ以上の提案が必要です。さて、私が考えたのはこれです:スレッドをいくつか作ってみましょう(thread_a_gogoまたはウェブワーカーを使用して)。これで、さらに多くが必要になったときに、さらに作成できます。ただし、作成プロセスにはある程度の制限があります。 (システムによって暗示されないが、おそらくオーバーヘッドのため)。これで、制限を超えたときに、新しいノードを分岐し、その上にスレッドの作成を開始できます。この方法では、制限に達するまで続行できます(結局、プロセスにも大きなオーバーヘッドがあります)。この制限に達すると、タスクのキューイングを開始します。スレッドが解放されるたびに、新しいタスクが割り当てられます。これにより、スムーズに続行できます。

だから、それは私が考えていたものでした。このアイデアは良いですか?私はこのプロセスとスレッドのすべてに少し慣れていないので、専門知識はありません。ご意見をお聞かせください。

ありがとう。 :)

109
Parth Thakkar

Nodeにはまったく異なるパラダイムがあり、正しくキャプチャされると、問題を解決するこのさまざまな方法を見やすくなります。同じことを行う別の方法があるため、Node application(1)で複数のスレッドを必要としません。複数のプロセスを作成します。ただし、Apacheの方法とは非常に異なります。 WebサーバーのPrefork mpmはサポートします。

とりあえず、CPUコアが1つしかないので、いくつかの作業を行うために(Nodeの方法で)アプリケーションを開発すると考えてみましょう。私たちの仕事は、コンテンツをバイト単位で実行する大きなファイルを処理することです。ソフトウェアの最良の方法は、ファイルの先頭から作業を開始し、バイト単位で最後まで実行することです。

-ヘイ、ハサン、あなたは私の祖父の時代の初心者か非常に古い学校のどちらかだと思います!!!なぜいくつかのスレッドを作成して、もっと速くしてみませんか?

-ああ、CPUコアは1つしかありません。

- だから何?いくつかのスレッドを作成し、高速化してください!

-それはそのようには機能しません。スレッドを作成すると、速度が遅くなります。スレッドを切り替えるためにシステムに多くのオーバーヘッドを追加し、スレッドに時間を与えようとし、プロセス内でこれらのスレッド間で通信しようとするためです。これらすべての事実に加えて、単一のジョブを並列に実行できる複数の部分に分割する方法についても考えなければなりません。

-わかりました、わかりました、あなたは貧しいです。 32コアのコンピューターを使用してみましょう。

-うわー、あなたは私の親愛なる友人、素晴らしいです、どうもありがとう。それは有り難いです!

その後、仕事に戻ります。現在、私たちの豊かな友人のおかげで32 CPUコアがあります。順守しなければならない規則が変更されました。今、私たちは与えられたこのすべての富を活用したいと思います。

複数のコアを使用するには、作業を並列に処理できる部分に分割する方法を見つける必要があります。 Nodeでない場合、これにスレッドを使用します。 CPUコアごとに1つ、32スレッド。ただし、Nodeがあるため、32 Nodeプロセスを作成します。

スレッドはNodeプロセスの良い代替手段かもしれませんが、もっと良い方法かもしれませんが、作業が既に定義されており、処理方法を完全に制御できる特定の種類のジョブでのみです。これ以外に、私たちが制御できない方法でジョブが外部から来て、できるだけ早く答えたい他のすべての種類の問題について、Nodeの方法は間違いなく優れています。

-ヘイ、ハサン、まだシングルスレッドで作業していますか?あなたは何が悪いのですか?あなたが望むものをあなたに提供しました。もう言い訳はありません。スレッドを作成し、実行を高速化します。

-作業をいくつかの部分に分割しましたが、すべてのプロセスがこれらの部分の1つで並行して動作します。

-スレッドを作成してみませんか?

-申し訳ありませんが、使用できるとは思いません。必要に応じてコンピューターを使用できますか?

-大丈夫、私はクールです、なぜスレッドを使用しないのか理解できませんか?

-コンピューターをありがとう。 :)私はすでに作品を断片に分割し、これらの断片を並行して処理するプロセスを作成しています。すべてのCPUコアが完全に使用されます。プロセスの代わりにスレッドでこれを行うことができます。しかしNodeはこの方法であり、上司のParth ThakkarはNodeの使用を望んでいます。

-さて、別のコンピューターが必要かどうか教えてください。 :p

32個ではなく33個のプロセスを作成すると、オペレーティングシステムのスケジューラーがスレッドを一時停止し、もう1つを開始し、数サイクル後に一時停止し、もう1つを再開します。これは不要なオーバーヘッドです。いりません。実際、32コアのシステムでは、正確に32個のプロセスを作成したくはありません。31個はnicerです。このシステムで動作するのは私のアプリケーションだけではないからです。特に32の部屋がある場合は、他の物のために小さな部屋を残すことは良いことです。

CPUを集中的に使用するタスクのためにプロセッサを完全に利用することについては、同じページにいると思います。

-うーん、ハサン、あなたを少しyou笑してすみません。私はあなたを今よりよく理解していると信じています。しかし、まだ説明が必要なものがあります。何百ものスレッドを実行することに関するすべての話題は何ですか?スレッドはプロセスをフォークするよりも作成と愚かさの方がはるかに速いことをどこでも読んでいますか?スレッドの代わりにプロセスをフォークし、Nodeで得られる最高のプロセスだと思います。次に、Nodeはこの種の作業に適していませんか?

-心配いりません、私もかっこいいです。誰もがこれらのことを言うので、私はそれらを聞くことに慣れていると思います。

- そう? Nodeはこれには向いていませんか?

-Nodeは、スレッドも優れている可能性がありますが、これには完全に適しています。スレッド/プロセス作成のオーバーヘッドに関しては、多くの繰り返しを行う場合、1ミリ秒ごとにカウントします。 32のプロセスがあり、わずかな時間がかかりますが、1回だけ発生し、違いはありません。

-では、何千ものスレッドをいつ作成したいのですか?

-数千のスレッドを作成する必要はありません。ただし、HTTP要求を処理するWebサーバーのように、外部からの作業を実行しているシステムでは、リクエストごとにスレッドを使用している場合、多くのスレッドを作成します。

-Nodeは違いますか?そうですか?

- はい、正確に。ここでNodeが本当に輝いています。スレッドがプロセスよりもずっと軽いように、関数呼び出しはスレッドよりもずっと軽いです。Node代わりに関数を呼び出しますWebサーバーの例では、着信要求ごとに関数呼び出しが発生します。

-うーん、面白い。ただし、複数のスレッドを使用していない場合は、同時に1つの関数しか実行できません。多くの要求が同時にWebサーバーに到着した場合、これはどのように機能しますか?

-関数を1つずつ実行し、2つを並列に実行する方法については完全に正しいです。つまり、単一のプロセスでは、一度に実行されるコードのスコープは1つだけです。 OSスケジューラは、プロセスを一時停止してプロセス内の別のスレッドではなく、別のプロセスに時間を与えない限り、この機能を一時停止して別の機能に切り替えません。 (2)

-では、プロセスは一度に2つの要求をどのように処理できますか?

-システムに十分なリソース(RAM、ネットワークなど)がある限り、プロセスは一度に何万ものリクエストを処理できます。これらの機能の実行方法が主な違いです。

-うーん、私は今興奮する必要がありますか?

-たぶん:) Nodeキューでループを実行します。このキューには、ジョブ、つまり着信要求の処理を開始した呼び出しがあります。ここで最も重要な点は、設計方法ですリクエストの処理を開始し、ジョブが完了するまで呼び出し元を待機させる代わりに、許容可能な量の作業を行った後、関数をすぐに終了します。いくつかの作業を行い、値を返すために、それを待つ代わりに、関数を終了して残りの作業をキューに追加します。

-複雑すぎますか?

-いいえ、いいえ、複雑に聞こえるかもしれません。しかし、システム自体は非常にシンプルであり、完全に理にかなっています。

ここで、これら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プロセスは、最初の脚注で述べたのと同じ理由で複数のスレッドを持っています。 IOイベントを受け入れ、プロセス間メッセージングを処理したい。

更新(コメントの良い質問への返信として)

@Mark、建設的な批判に感謝します。 Nodeのパラダイムでは、キュー内の他のすべての呼び出しが次々に実行されるように設計されていない限り、処理に時間がかかりすぎる関数を使用しないでください。計算負荷の高いタスクの場合、全体像を見ると、これは「スレッドまたはプロセスを使用すべきか?」の問題ではないことがわかります。しかし、「これらのタスクをバランスの取れた方法で、システム上の複数のCPUコアを使用して並列に実行できるサブタスクに分割するにはどうすればよいですか?」 8コアのシステムで400個のビデオファイルを処理するとします。一度に1つのファイルを処理する場合は、同じファイルの異なる部分を処理するシステムが必要になります。その場合は、マルチスレッドの単一プロセスシステムの方が構築が簡単で、さらに効率的です。状態共有/通信が必要なときに複数のプロセスを実行し、それらの間でメッセージを渡すことにより、これに対してNodeを使用できます。前に述べたように、Nodeは同様にこの種のタスクにおけるマルチスレッドアプローチですが、それ以上ではありません。前にも言ったように、Node shinesは、これらのタスクが複数のソースからシステムへの入力として来るときです。Node thread-per-connectionまたはprocess-per-接続システム。

setTimeout(...,0)呼び出しに関しては;時間のかかるタスク中に休憩を与えて、キュー内の呼び出しに処理の分担を許可することが必要になる場合があります。さまざまな方法でタスクを分割すると、これらからあなたを救うことができます。それでも、これは実際にはハックではなく、イベントキューが機能する方法です。また、この目的で_process.nextTick_を使用すると、setTimeoutを使用するときに、経過時間の計算とチェックが必要になりますが、_process.nextTick_は本当に必要なものです。タスク、キューの最後に戻り、共有を使用しました!」

327
hasanyasin

(Update 2016:Web Workers are going to io.js-Node.jsフォーク Node.js v7-以下を参照してください。)

(2017年更新:Webワーカーはnot Node.js v7またはv8に移行-以下を参照)

(2018年の更新:Webワーカーare Node.jsに入るNode v10.5.0-下記参照)

いくつかの説明

上記の回答を読んだ後、ウェブワーカーにはJavaScriptの一般的な哲学に反するものはなく、特に同時実行に関してNodeがあります。 「WHATWGで議論することすらできません。ブラウザにはあまり実装されていません」.

Webワーカーは、非同期でアクセスされる軽量のマイクロサービスと考えることができます。状態は共有されません。ロックの問題はありません。ブロッキングはありません。同期は必要ありません。 NodeプログラムからRESTfulサービスを使用する場合と同様に、RESTfulサービスが独自のイベントループと同じスレッドにないため、「マルチスレッド」になることを心配する必要はありません。非同期でアクセスする別個のサービスであり、それが重要です。

Webワーカーの場合も同じです。完全に独立したコンテキストで実行されるコードと通信するための単なるAPIであり、異なるスレッド、異なるプロセス、異なるcgroup、ゾーン、コンテナ、または異なるマシンにあるかどうかは、厳密に非同期の非ブロッキングAPIであるため、まったく無関係です。すべてのデータが値で渡されます。

実際のところ、Webワーカーは概念的にはNodeに最適です。多くの人が気づいていないように-偶発的にスレッドを非常に頻繁に使用します。 " - 見る:

しかし、Webワーカーはスレッドを使用して実装する必要さえありません。 Web Worker APIが使用されている限り、クラウドでプロセス、グリーンスレッド、またはRESTfulサービスを使用できます。値渡しセマンティクスを使用したメッセージパッシングAPIの全体的な美しさは、同時実行モデルの詳細が公開されないため、基礎となる実装がほとんど無関係であることです。

シングルスレッドのイベントループは、I/Oバウンド操作に最適です。 CPUにバインドされた操作、特に長時間実行される操作ではうまく機能しません。そのためには、より多くのプロセスを生成するか、スレッドを使用する必要があります。子プロセスとプロセス間通信をポータブルな方法で管理することは非常に困難な場合があり、単純なタスクではやり過ぎと見なされることがよくありますが、スレッドを使用することは、正しく実行するのが非常に難しいロックと同期の問題に対処することを意味します.

頻繁に推奨されるのは、長時間実行されるCPUバインド操作を小さなタスクに分割することです( my set up to setInterval の「Original answer」セクションの例のようなもの)が、常に実用的ではありませんまた、複数のCPUコアを使用しません。

基本的に、Webワーカーはサーバーではなくブラウザー用に作成されていると言っているコメントを明確にするために書いています(JavaScriptのほとんどすべてについて言えることを忘れて)。

ノードモジュール

NodeにWeb Workersを追加することになっているいくつかのモジュールがあります:

いずれも使用していませんが、関連する可能性のある2つの簡単な観察結果があります。2015年3月現在、node-webworkerは4年前に最後に更新され、node-webworker-threadsは1か月前に最後に更新されました。また、node-webworker-threadsの使用例では、Workerコンストラクターの引数としてファイル名の代わりに関数を使用できることがわかります。これは、メモリを共有するスレッドを使用して実装すると微妙な問題を引き起こす可能性があります関数は.toString()メソッドにのみ使用され、それ以外の場合は別の環境でコンパイルされます。その場合は問題ないかもしれません。ここで観察結果を共有するだけで、より深く調べる必要があります)。

NodeにWeb Workers APIを実装する他の関連プロジェクトがある場合は、コメントを残してください。

アップデート1

執筆時点ではまだ知りませんでしたが、偶然にもこの回答を書く前の1日 Web Workersがio.jsに追加されました

io.js はNode.jsのフォークです。詳細については、 io.jsがNode.jsをフォークすることに決めた理由 を参照してください。 )

一般にJavaScriptの哲学に反するWebワーカーには何もないという点を証明するだけでなく、特に同時実行に関してNode io.jsのようなサーバーサイドJavaScriptの市民(および、将来的にはNode.jsも)は、クライアントサイドJavaScriptに既にあるように すべての最新のブラウザーで です。

更新2

Update 1および my Tweet を参照していました io.js pull request#1159 これは Node PR#1159 にリダイレクトされました7月8日、 Node PR#21 に置き換えられます-これはまだ開いています。プルリクエストの下で、io.js/Node.jsのWebワーカーのステータスに関する最新情報を提供するディスカッションがいくつか行われています。

アップデート3

最新情報-コメントに投稿してくれたNiCk Newmanに感謝: workers:initial implementation 2015年9月6日のPetka Antonovによるコミット。ダウンロードして this tree で試用できます。詳細については、 NiCk Newmanによるコメント を参照してください。

更新4

2016年5月現在まだ開かれていることに関する最後のコメント PR#2133-労働者:初期導入 は3か月前でした。 5月30日に、Matheus Moreiraが、この回答の更新を下のコメントに投稿するように私に依頼し、彼は この機能の現在の状態を尋ねました のPRコメントに。

PRディスカッションの最初の回答は懐疑的でしたが、後にBen Noordhuis wrote 「v7のtodoリストには、これを何らかの形でマージすることが含まれています」と答えました。

他のすべてのコメントは2番目のようであり、2016年7月の時点で、 Web WorkersはNode の次のバージョンで利用可能になり、バージョン7.0は 2016年10月(必ずしもこの正確なPRの形式ではありません)。

コメントで指摘し、GitHubでの議論を復活させてくれたMatheus Moreiraに感謝します。

更新5

2016年7月の時点で、npmには以前は利用できなかったモジュールがほとんどありません-関連モジュールの完全なリストについては、 npm ワーカー、ウェブワーカー、など。特に何かがうまくいかない場合は、コメントを投稿してください。

アップデート6

2017年1月の時点で、WebワーカーがNode.jsにマージされる可能性は低いです。

プルリクエスト#2133 ワーカー:初期実装 2015年7月8日のPetka Antonovによる最終的に closed 12月のBen Noordhuisによる2016年11月11日、「マルチスレッドのサポートにより、十分な利益が得られないほど多くの新しい障害モードが追加される」と「共有メモリやより効率的なシリアル化などの従来の方法を使用しても達成できます」とコメントしました。

詳細については、GitHubの PR 21 へのコメントを参照してください。

コメントで指摘してくれたMatheus Moreiraに再び感謝します。

アップデート6

数日前、 June 2018 で、ウェブワーカーがNode v10.5.0で、_--experimental-worker_フラグ。

詳細については、以下を参照してください。

????????????最後に! 3年前のStack Overflowの回答の7回目の更新で、ウェブワーカーのスレッド化はNode哲学ではなく、今回はようやくわかった!?? ??????

33
rsp

私は、マルチスレッドを使用してソフトウェアを高速化した古い考え方の出身です。過去3年間、私はNode.jsとその大きな支持者を使用しています。 hasanyasinがノードの仕組みと非同期機能の概念を詳細に説明したように。ただし、ここにいくつか追加します。

昔、シングルコアと低いクロック速度で、ソフトウェアを高速かつ並列に動作させるためのさまざまな方法を試しました。 DOS時代には、一度に1つのプログラムを実行するために使用します。 Windowsよりも、複数のアプリケーション(プロセス)を一緒に実行し始めました。テストされている場合、プリエンプティブおよび非プリエンプティブ(または協同)などの概念。プリエンプティブが、シングルコアコンピューターでのより良いマルチプロセッシングタスクの答えであることがわかりました。プロセス/タスクとコンテキストスイッチングの概念が登場しました。スレッドの概念よりも、プロセスコンテキストの切り替えの負担をさらに軽減します。新しいプロセスの生成に代わる軽量の代替として造られたスレッド。

つまり、シグナルスレッドであるかどうか、またはマルチコアやシングルコアではないことは、OSによってプロセスが横取りされ、タイムスライスされることになります。

Nodejsは単一のプロセスであり、非同期メカニズムを提供します。ここでは、タスクが完了するのをイベントループで待っている間に、横になっているOSの下にジョブがディスパッチされてタスクが実行されます。 OSから緑色の信号を取得したら、必要なことを実行します。ある意味では、これは協調的/非プリエンプティブマルチタスクであるため、非常に長い時間イベントループをブロックしないでください。そうしないと、アプリケーションが非常に速く劣化します。
したがって、本来ブロックされているタスクや非常に時間がかかるタスクがある場合、OSとスレッドのプリエンプティブな世界に分岐する必要があります。この良い例は libuv documentation にあります。また、ドキュメントをさらに読むと、 FileI/Oはnode.jsのスレッドで処理されます であることがわかります。

そのため、まずソフトウェアの設計にそのすべてがあります。第二に、コンテキストの切り替えは、彼らが何を言っても常に発生します。スレッドが存在する理由はまだあります。その理由は、プロセス間で切り替えるほうが速いためです。

Node.jsの内部では、すべてのC++およびスレッド。また、ノードは、機能を拡張し、スレッドを使用することでさらに高速化するためのC++の方法を提供します。つまり、ソースからソースへの読み取り、大規模データ分析などのタスクをブロックします。

Hasanyasinの答えが受け入れられていることは知っていますが、私にとっては、あなたの言うことやスクリプトの背後に隠されている方法に関係なくスレッドが存在します。また、スレッドはNode.jsのバックボーンにあるため、完全にバッシングする前にマルチスレッドが正しい状態になります。また、スレッドはプロセスとは異なり、コアごとのノードプロセスの制限はスレッドの数には厳密に適用されません。スレッドはプロセスのサブタスクのようなものです。実際、スレッドは勝ちました; Windowsタスクマネージャまたはlinux topコマンドには表示されません。繰り返しますが、プロセスよりも重量が少ないです

8
limplash

Node.jsはサーバー上で実行されますが、この場合、webworkersが関連するかどうかはわかりませんが、クライアント側の技術(ブラウザーで実行)です。私が理解している限り、ファイバーもブロックしています。つまり、任意のマルチタスキングなので、使用できますが、yieldを介してコンテキストスイッチを管理する必要があります。スレッドは実際に必要なものかもしれませんが、node.jsでどれだけ成熟しているかはわかりません。

4
lanzz

worker_threadsが実装され、[email protected]。まだ初期の実装であり、将来のリリースでより効率的にするためには、より多くの努力が必要です。最新のnodeで試してみる価値があります。

3
motss

多くのNode開発者の意見では、Nodeの最良の部分の1つは、実際にはシングルスレッドの性質です。スレッドは、Nodeが非ブロッキングIOのみを行うことで完全に回避する共有リソースに多くの困難をもたらします。

それは、Nodeが単一のスレッドにlimitedであると言うことではありません。スレッド化された同時実行性を取得する方法が、探しているものと異なるだけです。スレッドを処理する標準的な方法は、Node自体に標準で付属している cluster モジュールを使用することです。コード内でスレッドを手動で処理するよりも、スレッドへのアプローチが単純です。

コード内で非同期プログラミングを処理するには(ネストされたコールバックピラミッドを回避するなど)、 Fibers ライブラリの[Future]コンポーネントが適切な選択です。また、ファイバーに基づく Asyncblock を確認することをお勧めします。ファイバーは、スタックを複製し、必要に応じてシングルスレッドでスタック間をジャンプすることでコールバックを非表示にできるため、優れています。利点を提供しながら、実際のスレッドの手間を省きます。欠点は、ファイバーを使用するとスタックトレースが少し奇妙になる可能性があることですが、それほど悪くはありません。

非同期のことを心配する必要がなく、ブロックせずに多くの処理を行うことにもっと興味がある場合は、時々process.nextTick(callback)を単純に呼び出すだけで十分です。

2
genericdave

実行しているタスクに関する詳細情報が役立つ場合があります。 (genericdaveの答えへのコメントで述べたように)何千ものそれらを作成する必要があるのはなぜですか? Nodeでこの種のことを行う通常の方法は、常に実行され、メッセージを使用して通信できるワーカープロセス(forkまたは他のメソッドを使用)を起動することです。実行中のタスクを実行する必要があるたびに新しいワーカーを起動するのではなく、既に実行中のワーカーにメッセージを送信し、完了時に応答を取得するだけです。最大数千の実際のスレッドは非常に効率的ですが、CPUによって制限されます。

今、すべてのことを言った後、私は Hook.io で最近多くの仕事をしてきましたが、これはこの種のオフロードタスクを他のプロセスに非常にうまくいくようですあなたが必要なもの。

1
kbjr