web-dev-qa-db-ja.com

JavaScriptはシングルスレッドなので、HTML5のWebワーカーはどのようにマルチスレッドを実行していますか?

私はHTML5のWebワーカーについて読んでいますが、JavaScriptがシングルスレッドであることを知っています。

私の質問は:

では、ウェブワーカーはどのようにマルチスレッドの作業を行っていますか?それとも、本当にマルチスレッドではない場合、どのようにシミュレートしますか?私にはここがはっきりしないようです。

57
James Drinkard

すでにいくつかのコメントが指摘しているように、ワーカーは実際にはマルチスレッドです。

あなたの考えを明確にするのに役立つかもしれないいくつかのポイント:

  • JavaScriptは言語であり、スレッドモデルを定義していません。必ずしもシングルスレッドではありません。
  • ほとんどのブラウザは、歴史的にシングルスレッドでした(ただし、急速に変化しています: [〜#〜] ie [〜#〜]ChromeFirefox )、そしてほとんどのJavaScript実装はブラウザーで発生します
  • WebワーカーはJavaScriptの一部ではなく、JavaScriptを介してアクセスできるブラウザー機能です
60
robertc

少し遅れましたが、私は同じ質問をしただけで、次の答えが思い付きました。
ブラウザのJavascriptは常にシングルスレッドであり、基本的な結果として、変数への「同時」アクセス(マルチスレッドの主な頭痛の種)があります。プログラミング)は実際には並行ではありません。これは当てはまります。ただし、webworkersは例外で、実際には個別のスレッドで実行され、変数への同時アクセスはやや明示的な方法で処理されました

私はJavaScript忍者ではありませんが、ブラウザのJavaScriptは単一のスレッドプロセスとして提供されており、それが本当かどうか、またはこの信念の背後にある理論的根拠にはあまり注意を払っていません。
この仮定をサポートする簡単な事実は、JavaScriptでプログラミングするとき共有変数への同時アクセスを気にする必要がないことです。問題を意識することなく、すべての開発者は、変数へのすべてのアクセスに一貫性があるかのようにコードを記述します。
つまり、いわゆる メモリモデル について心配する必要はありません。

実際、JavaScriptで並列処理を行うためにWebWorkersを見る必要はありません。 (非同期)AJAXリクエストを考えてください。変数への同時アクセスをどれほど不注意に処理するかを考えてください:

_var counter = 0;

function asyncAddCounter() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4) {
      counter++;
    }
  };
  xhttp.open("GET", "/a/remote/resource", true);
  xhttp.send();
}

asyncAddCounter();
counter++;
_

プロセス終了時のcounterの値は何ですか? _2_です。 「同時に」読み取られたり書き込まれたりすることは問題ではなく、決して_1_にはなりません。つまり、counterへのアクセスは常に一貫しています。 2つのスレッドが実際に値に同時にアクセスする場合、どちらも_0_を読み取ることで開始し、最後に_1_を書き込むことができます。

ブラウザーでは、リモートリソースの実際のデータフェッチは開発者に隠されており、その内部の仕組みはJavaScript APIの範囲外です(ブラウザーでJavaScript命令に関して制御できるもの)。開発者に関する限り、ネットワーク要求のresultはメインスレッドによって処理されます。
要するに、リクエストの実際の実行は表示されませんが、callback(カスタムJavaScriptコードによる結果の処理)は、メインスレッドによって実行されます。
たぶん、それがウェブワーカーのためでなければ、「マルチスレッディング」という用語がJavascriptの世界に入ることはないでしょう。

リクエストの実行とコールバックの非同期呼び出しは、実際にはイベントループを使用して実現されます。マルチスレッドではありません。これはいくつかのブラウザに当てはまり、明らかにNode.jsにも当てはまります。以下は参考資料ですが、場合によっては少し時代遅れになっていますが、主な考え方は今も残っていると思います。

この事実が、JavaScriptが Event-driven であると言われているが、マルチスレッドではない理由です。
JavaScriptは非同期イディオムを許可しますが、parallelJavaScriptコード(ウェブワーカーの外部)の実行を許可しないことに注意してください。 asynchronousという用語は、2つの命令の結果がスクランブルされた順序で処理される可能性があることを示しています。

WebWorkersと同様、それらは開発者がマルチスレッドプロセスを制御できるようにするJavaScript APIです。
そのため、共有メモリへの同時アクセス(異なるスレッドでの値の読み取りと書き込み)を処理する明示的な方法を提供します。これは、とりわけ、次の方法で行われます。

  • 構造化クローンによってWebワーカーにデータをプッシュする(つまり、新しいスレッドがデータを読み取る) 構造化クローンアルゴリズム-Web API | MDN 。基本的に「共有」変数はなく、代わりに新しいスレッドにオブジェクトの新しいコピーが与えられます。
  • 値の所有権を転送することにより、データをWebワーカーにプッシュします: Transferable-Web APIs | MDN 。つまり、1つのスレッドだけがいつでもその値を読み取ることができます。
  • webワーカーによって返される結果(それらが「書き込む」方法)の場合と同様に、メインスレッドは(たとえば、thisWorker.onmessage = function(e) {console.log('Message ' + e.data + ' received from worker');}を使用して)要求されたときに結果にアクセスします。それは通常のイベントループによるものである必要があると思います。
  • メインスレッドとWebワーカーは、真に共有されているメモリSharedArrayBufferにアクセスします。これは、Atomic関数を使用してスレッドセーフにアクセスされます。私はこれがこの記事で明らかに公開されていることを発見しました: JavaScript:ワーカーから共有メモリへ
  • 注:Webワーカーは、本当に共有されているDOMにアクセスできません。
8
Antonio

.jsファイルを「ワーカー」として生成し、別のスレッドでプロセスを実行します。 JSONデータを「メイン」スレッドとの間でやり取りできます。ただし、ワーカーはDOMなどの特定のものにアクセスできません。

したがって、たとえば、複雑な数学の問題を解決したい場合は、ユーザーがブラウザに入力して、それらの変数をワーカーに渡し、メインスレッドでユーザーに実行させながら、バックグラウンドで計算を行わせることができます他のもの、またはプログレスバーなどを表示し、ワーカーが完了すると、回答を返し、ページに印刷します。複数の問題を非同期で実行し、回答が順番通りに返されない場合もあります。かなりすっきり!

5
JKing

ブラウザは、実行するJavaScriptを含むスレッドを開始します。つまり、これは本当のスレッドであり、このWebワーカーのことで、あなたのjsはもはやシングルスレッドではありません。

0
rapadura