JavaScriptのマルチスレッド

Christopher Nolan
Christopher Nolanオリジナル
2025-02-25 11:37:12887ブラウズ

JavaScriptのマルチスレッド

わかりました、始める前に、きれいになって、この記事のタイトルが少しセンセーショナルなことを認めましょう! JavaScriptにはマルチスレッド機能が実際にはなく、JavaScriptプログラマーがそれを変更することができることは何もありません。すべてのブラウザで - Google Chromeを除いて - JavaScriptは単一の実行スレッドで実行されます。

しかし、私たちにできることは、マルチスレッド環境の利点の1つを引き起こす限り、

マルチスレッドをシミュレートします。これは、それ以外の場合はブラウザをフリーズアップし、Firefoxでそれらの「反応しないスクリプト」警告の1つを生成するコードです。 キーテイクアウト

javaScriptはマルチスレッドをネイティブにサポートするのではなく、非同期タイマーとWebワーカーを通じてそれをシミュレートし、ブラウザを凍らせずに集中的な計算を許可します。

非同期タイマーはタスクをより小さなチャンクに分解し、コードが時間の経過とともに実行する方法を管理することでブラウザが反応しなくなるのを防ぎます。
    Webワーカーは、ユーザーインターフェイスに影響を与えることなくバックグラウンドタスクを実行するJavaScriptの機能を強化しますが、DOMと対話したり、特定のWeb APIを使用したりすることはできません。 ブラウザをロックするリスクのある複雑な操作の場合、コードをリファクタリングして非同期タイマーを使用すると、UIフリーズを防ぎ、リソース集約型プロセスをより効果的に管理できます。
  • JavaScriptの単一スレッド性にもかかわらず、非同期プログラミングやWebワーカーなどの技術は、マルチスレッドのようなシナリオを処理するための堅牢なツールを開発者に提供し、集中的なタスクのパフォーマンスを向上させます。
  • 時間は誰も待っていません
  • それはすべて、非同期タイマーの使用にかかっています。非同期タイマー内で繰り返しコードを実行すると、ブラウザのスクリプトインタープリターの時間に各反復を処理する時間を与えています。
  • 効果的に、Iteratorの内部のコードは、通訳者にすべてをすぐに実行するよう求めています。ただし、非同期タイマー内の同じコードは、コードを小さく控えめなチャンクに分割しています。つまり、「このコードを可能な限り速く実行する」 - 待ってから、「できるだけ速くこのコードを実行する」など、
  • n
  • トリックは、各反復内のコードが小さく、インタープリターがタイマーの速度内で完全に処理するのに十分なほど単純であることです。その要件が満たされている場合、コード全体がどれほど激しいかは関係ありません。なぜなら、一度に実行するように要求していないからです。
    「強すぎる」どれだけ強烈ですか?

    通常、私が集中的すぎることが証明されたスクリプトを書いている場合、私はそれを再設計することを見ています。このような重要な減速は、通常、コードの問題、またはアプリケーションの設計に関するより深い問題を示しています。

    しかし、時にはそうではありません。時々、特定の操作の強度を避ける方法がありません。

    それは、特定のケースで最良の解決策かもしれません。おそらく、アプリケーションの一部の処理はサーバー側に移動する必要があります。そこでは、一般的に、そして一般的にスレッドされた実行環境(Webサーバー)を使用するためのより多くの処理能力があります。

    しかし、最終的には、それがオプションではない状況を見つけるかもしれません。javaScriptが単に

    何かをすることができなければならないか、気の毒に思う必要があります。それは、私のFirefox拡張機能、Dust-Meセレクターを開発するときに私が見つけた状況です。 その拡張機能のコアは、ページに適用されるCSSセレクターをテストし、実際に使用されているかどうかを確認する機能です。これの本質は、Dean Edwards 'Base2:のMatchall()メソッドを使用した一連の評価です。

    確かに、

    十分に簡単です。しかし、Matchall()自体は、CSS1またはCSS2セレクターを解析して評価し、Matchを探してDOMツリー全体を歩きます。拡張機能は、個々のセレクター

    for(var i=0; i<selectors.length; i++) <br>
    { <br>
      if(base2.DOM.Document.matchAll <br>
        (contentdoc, selectors[i]).length > 0) <br>
      { <br>
        used ++; <br>
      } <br>
      else <br>
      { <br>
        unused ++; <br>
      } <br>
    }
    に対してそれを行います。そのプロセスは、表面上で非常に単純で、ブラウザ全体が発生中にフリーズするほど集中的になる可能性があります。そして、これが私たちが見つけたものです。

    ブラウザをロックすることは明らかにオプションではないので、これが機能する場合は、エラーなしで実行する方法を見つけなければなりません。 簡単なテストケース

    2つのレベルの反復を含む簡単なテストケースで問題を示しましょう。内部レベルは意図的に集中的すぎるため、レース条件を作成できますが、外側のレベルはかなり短いため、メインコードをシミュレートできます。これが私たちが持っているものです:

    テストを開始し、簡単なフォームから出力を取得します(これはテストコードであり、生産ではないので、インラインイベントハンドラーの使用を頼ることを許してください):

    function process() <br>
    { <br>
      var above = 0, below = 0; <br>
      for(var i=0; i<200000; i++) <br>
      { <br>
        if(Math.random() * 2 > 1) <br>
        { <br>
          above ++;       <br>
        } <br>
        else <br>
        { <br>
          below ++; <br>
        } <br>
      } <br>
    } <br>
     <br>
     <br>
    function test1() <br>
    { <br>
      var result1 = document.getElementById('result1'); <br>
       <br>
      var start = new Date().getTime(); <br>
         <br>
      for(var i=0; i<200; i++) <br>
      { <br>
        result1.value =  'time=' +  <br>
          (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
         <br>
        process(); <br>
      } <br>
       <br>
      result1.value = 'time=' +  <br>
        (new Date().getTime() - start) + ' [done]'; <br>
    }
    次に、そのコードをFirefoxで実行しましょう(この場合、2GHz MacBookのFirefox 3)...予想通り、ブラウザのUIが実行中にフリーズします(たとえば、プロセスを更新して放棄することは不可能になります) 。約90回の反復後、Firefoxは「反応しないスクリプト」警告ダイアログを作成します。

    <form action=""> <br>
      <fieldset> <br>
        <input type="button" value="test1" onclick="test1()" /> <br>
        <input type="text"  /> <br>
      </fieldset> <br>
    </form> <br>
    

    それを許可する場合、さらに90回の反復後、Firefoxが同じダイアログを再び生成します。この点で、Safari 3とInternet Explorer 6が同様に動作し、凍結したUIと警告ダイアログが作成されるしきい値で動作します。 Operaでは、そのようなダイアログはありません。コードが完了するまでコードを実行し続けていますが、ブラウザUIはタスクが完了するまで同様に冷凍されます。

    明らかに、実際にはそのようなコードを実行することはできません。それでは、それを再要素して、アウターループに非同期タイマーを使用しましょう:

    もう一度実行しましょう...今回はまったく異なる結果を受け取ります。コードは確かに完了するのに時間がかかりますが、UIが凍結し、過度に遅いスクリプトについて警告なしに、最後まで正常に実行されます。
    for(var i=0; i<selectors.length; i++) <br>
    { <br>
      if(base2.DOM.Document.matchAll <br>
        (contentdoc, selectors[i]).length > 0) <br>
      { <br>
        used ++; <br>
      } <br>
      else <br>
      { <br>
        unused ++; <br>
      } <br>
    }

    テストページを表示します

    (忙しいフラグは、タイマーインスタンスが衝突するのを防ぐために使用されます。次の反復が来るときにサブプロセスの真ん中にいる場合は、次の反復を待つだけで、1つだけを保証するだけです。サブプロセスは一度に実行されています。)

    ご覧のとおりですが、

    内側

    プロセスでできる作業はまだ最小限ですが、

    そのプロセスを実行できます。外側のループは基本的に永遠に、ブラウザは決してフリーズしません。 それはそれに似ています - 私たちはこれを野生で使用できます。

    あなたは狂っています!

    私はすでに反対者を聞くことができます。実際、私は自分自身になることができます。なぜあなたはこれをするのですか?どのような狂った人がJavaScriptをこれらすべての場所に押し進めることを主張することを主張します。あなたのコードはあまりにも激しいです。これは仕事の間違ったツールです。これらの種類のフープを飛び越えなければならない場合、アプリケーションの設計は根本的に間違っています。

    既に、重いスクリプトが仕事をする方法を見つけなければならなかった1つの例について言及しました。それは、または全体のアイデアを放棄しなければならなかったかのいずれかでした。あなたがその答えに確信していない場合、記事の残りの部分もあなたにアピールしないかもしれません。

    しかし、あなたがいる場合、または少なくとも、あなたが確信を持つことを受け入れている場合は、実際にそれを家に釘付けにする別の例があります:JavaScriptを使用してコンピューターと対戦できるゲームを書くことができます。

    ゲーム

    ここで私が話しているのは、ゲームのルールを理解するために必要なコードです。その後、そのゲームであなたを打ち負かすために状況と戦術を評価できます。複雑なもの。

    説明するために、私がしばらくの間、私が開発してきたプロジェクトを見ていきます。 「少しの間」とは、 JavaScriptのマルチスレッド

    要約すると、隣接する形状と色の一致によってボード全体に進みます。たとえば、たとえば緑色の三角形を開始すると、他の三角形、または他の緑の形状に移動できます。あなたの目的は、中央のクリスタルに到達し、それをボードの反対側に持って行くことです。相手は同じことをしようとします。相手からクリスタルを盗むこともできます したがって、動きを決定する論理的ルールがあり、戦術が出現することもあります。たとえば、相手にクリスタルに到達させたり、クリスタルを盗んだりすることを避けるために、ブロックする動きを選択したり、到達できない場所で終了しようとします。

    コンピューターの作業は、特定の状況に最適な動きを見つけることです。そのため、そのプロセスを要約の擬似コードに見てみましょう。

    戦術を評価します。それが私たちに良い動きを与えた場合、私たちは完了です。それ以外の場合は、別の戦術を評価するか、動きがあるか、それがなくても合格しなければならないと結論付けるまで評価します。

    これらの戦術関数のそれぞれは、ボード上のすべての位置と、潜在的な将来の位置を評価する必要があるため、それぞれさまざまな要因に照らして何度も潜在的な将来の位置を評価する必要があるため、高価なプロセスを実行します。この例には3つの戦術しかありませんが、実際のゲームでは、数十の異なる可能性があり、それぞれが評価するのに費用がかかります。
    for(var i=0; i<selectors.length; i++) <br>
    { <br>
      if(base2.DOM.Document.matchAll <br>
        (contentdoc, selectors[i]).length > 0) <br>
      { <br>
        used ++; <br>
      } <br>
      else <br>
      { <br>
        unused ++; <br>
      } <br>
    }
    これらの評価のいずれかが個別に問題ありませんが、それらすべてが連続して実行され、ブラウザをフリーズする過度に激しいプロセスを作成します。

    では、メインコードを控えめなコード

    タスクに分割することでした

    このコードは元のコードよりもはるかに冗長です。したがって、コードサイズの削減が唯一の不可欠である場合、これは明らかに道ではありません。

    しかし、ここでやろうとしているのは、天井のない実行環境、つまり複雑さと長さの点で上限のないプロセスを作成することです。そして、それが私たちがやったことです。 このパターンは、数百または数千のタスクで、無期限に

    無期限に拡張できます。実行するのに長い時間がかかるかもしれませんが、実行するには実行されます。各
    function process() <br>
    { <br>
      var above = 0, below = 0; <br>
      for(var i=0; i<200000; i++) <br>
      { <br>
        if(Math.random() * 2 > 1) <br>
        { <br>
          above ++;       <br>
        } <br>
        else <br>
        { <br>
          below ++; <br>
        } <br>
      } <br>
    } <br>
     <br>
     <br>
    function test1() <br>
    { <br>
      var result1 = document.getElementById('result1'); <br>
       <br>
      var start = new Date().getTime(); <br>
         <br>
      for(var i=0; i<200; i++) <br>
      { <br>
        result1.value =  'time=' +  <br>
          (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
         <br>
        process(); <br>
      } <br>
       <br>
      result1.value = 'time=' +  <br>
        (new Date().getTime() - start) + ' [done]'; <br>
    }
    個々のタスクがそれほど激しくない限り、ブラウザを殺さずに実行されます。

    returnのパス

    このアプローチの強さも主要な弱点です。内的関数は非同期であるため、外部関数から値を返すことはできません。したがって、たとえば、私たちはこれを行うことはできません(またはむしろ、できますが、意味はありません):

    for(var i=0; i<selectors.length; i++) <br>
    { <br>
      if(base2.DOM.Document.matchAll <br>
        (contentdoc, selectors[i]).length > 0) <br>
      { <br>
        used ++; <br>
      } <br>
      else <br>
      { <br>
        unused ++; <br>
      } <br>
    }
    CheckSomThines()関数は、内部関数が非同期であるため、常にfalseを返します。外部関数は、内部関数の最初の反復が起こる前に戻ります!

    この次の例は、同様に無意味です:

    外部関数の範囲外であるため、そこから戻ることはできません。その返品値は、エーテルに役に立たなく消えます。

    function process() <br>
    { <br>
      var above = 0, below = 0; <br>
      for(var i=0; i<200000; i++) <br>
      { <br>
        if(Math.random() * 2 > 1) <br>
        { <br>
          above ++;       <br>
        } <br>
        else <br>
        { <br>
          below ++; <br>
        } <br>
      } <br>
    } <br>
     <br>
     <br>
    function test1() <br>
    { <br>
      var result1 = document.getElementById('result1'); <br>
       <br>
      var start = new Date().getTime(); <br>
         <br>
      for(var i=0; i<200; i++) <br>
      { <br>
        result1.value =  'time=' +  <br>
          (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
         <br>
        process(); <br>
      } <br>
       <br>
      result1.value = 'time=' +  <br>
        (new Date().getTime() - start) + ' [done]'; <br>
    }
    ここでできることは、ajaxコーディング技術から葉を取り出して、コールバック関数を使用します(この例では「oncomplete」と呼んでいます):

    したがって、checksomhings()を呼び出すと、匿名関数をその引数として渡します。ジョブが完了したときにその関数は最終値で呼び出されます。 エレガント?いいえ。しかし、堅牢に機能しますか?はい。そして、それがポイントです。この手法を使用して、そうでなければ不可能なスクリプトを書くことができます。

    Androidsはシリコン羊の夢を夢見ていますか?
    <form action=""> <br>
      <fieldset> <br>
        <input type="button" value="test1" onclick="test1()" /> <br>
        <input type="text"  /> <br>
      </fieldset> <br>
    </form> <br>
    

    このテクニックには、キットにあるため、以前は可能性の領域から抜け出したJavaScriptプロジェクトに取り組む手段があります。私がこのパターンを開発したゲームにはかなり単純なロジックがあり、したがってかなり単純な

    function test2() <br>
    { <br>
      var result2 = document.getElementById('result2'); <br>
       <br>
      var start = new Date().getTime(); <br>
       <br>
      var i = 0, limit = 200, busy = false; <br>
      var processor = setInterval(function() <br>
      { <br>
        if(!busy) <br>
        { <br>
          busy = true; <br>
           <br>
          result2.value =  'time=' +  <br>
            (new Date().getTime() - start) + ' [i=' + i + ']'; <br>
           <br>
          process(); <br>
           <br>
          if(++i == limit) <br>
          { <br>
            clearInterval(processor); <br>
     <br>
            result2.value = 'time=' +  <br>
              (new Date().getTime() - start) + ' [done]'; <br>
          } <br>
           <br>
          busy = false; <br>
        } <br>
         <br>
      }, 100); <br>
       <br>
    }
    がありますが、従来の反復にはまだ多すぎました。そして、もっと多くの影響力が必要な他のゲームがたくさんあります!

    私の次の計画は、この手法を使用してJavaScriptチェスエンジンを実装することです。チェスには、可能なシナリオと戦術が幅広く揃っているため、このテクニックなしで実行可能であったよりもはるかに長い間、計算に非常に長い時間がかかる可能性がある決定につながります。最も基本的な思考マシンを作成するには激しい計算が必要であり、私は可能性に非常に興奮していることを告白します。

    このようなトリックをやってのけることができれば、誰が何が可能かを言うのは誰ですか?自然言語処理、ヒューリスティック…おそらく、JavaScriptで人工知能を開発するためのビルディングブロックがあります!この投稿を読んで楽しんだなら、学習できるのが大好きです。マスターから新鮮なスキルとテクニックを学ぶ場所。メンバーは、Web用のJavaScriptプログラミングなど、SitePointのすべての電子ブックやインタラクティブなオンラインコースに即座にアクセスできます。 この記事へのコメントは閉じられています。 JavaScriptについて質問がありますか?フォーラムで聞いてみませんか? 画像クレジット:ランデンLピーターソン javascriptのマルチスレッドに関するよくある質問

    JavaScriptマルチスレッドにおけるWebワーカーの役割は何ですか?

    Webワーカーは、JavaScriptマルチスレッドで重要な役割を果たします。これらは、Webコンテンツがバックグラウンドスレッドでスクリプトを実行する簡単な手段です。ワーカースレッドは、ユーザーインターフェイスに干渉することなくタスクを実行できます。さらに、xmlhttprequestを使用してI/Oを実行できます(ただし、ResponseXMLおよびチャネル属性は常にnullです)。作成されたら、ワーカーは、そのコードで指定されたイベントハンドラーにメッセージを投稿することによって作成されたJavaScriptコードにメッセージを送信できます(およびその逆)。 🎜>

    JavaScriptは本質的にシングルスレッドですが、非同期コールバックと約束を使用してマルチスレッドを処理できます。つまり、JavaScript自体は単一のスレッドで動作しますが、将来実行するタスクをスケジュールし、効果的に複数のタスクを同時に実行できるようにすることを意味します。これは、メインスレッドが他のコードを実行し続けている間にバックグラウンドで処理できるユーザー入力やAPIリクエストなどの操作を処理するのに特に便利です。 🎜> JavaScriptのマルチスレッドはWebワーカーを通じて達成できますが、これらの労働者はDOMまたは他のWeb APIにアクセスできないことに注意することが重要です。それらは、メインスレッドに前後に送信できるデータ型のみに限定されています。また、各ワーカーは個別のインスタンスであるため、範囲やグローバル変数を共有しません。 「クラスター」と呼ばれるモジュールでは、メインノードプロセス(マスター)とサーバーポートを共有する子プロセス(ワーカー)を作成できます。これらの子プロセスは同時に実行され、さまざまなタスクで動作し、マルチスレッドを効果的に実装できます。データ操作の問題。マルチスレッドは、出力が他の制御不能なイベントのシーケンスやタイミングに依存するレース条件などの問題につながる可能性があります。スレッド間の依存関係に対処する必要がないため、単一スレッド環境を最適化する方が簡単だと考えられていました。

    JavaScriptでマルチスレッドにWebワーカーを使用するにはどうすればよいですか?

    JavaScriptでマルチスレッドにWebワーカーを使用するには、新しいワーカーオブジェクトを作成し、ワーカースレッドで実行されるJavaScriptファイルを指定する必要があります。その後、ポストメッサージメソッドを使用してワーカースレッドと通信し、オンメッセージイベントハンドラーを使用してメッセージからメッセージを受信できます。より速いですが、実行中のタスクの性質に依存します。 CPU集約型タスクの場合、マルチスレッドは、タスクを並行して実行できるようにすることで、パフォーマンスを大幅に改善できます。ただし、I/Oバウンドタスクの場合、これらのタスクはネットワーク速度などのCPUの制御以外の要因によって制限されることが多いため、マルチスレッドの利点はあまり顕著ではありません。 javascriptでのプログラミング?

    マルチスレッドと非同期プログラミングは、両方とも複数のタスクを同時に管理するために使用される手法です。ただし、さまざまな方法で行います。マルチスレッドには複数の実行スレッドが含まれ、各スレッドは異なるタスクを実行します。一方、非同期プログラミングには実行の単一のスレッドが含まれますが、タスクを開始してから保留にして後で完了し、その間に他のタスクを実行できるようにします。 javascriptのスレッド間のデータ共有?

    JavaScriptのスレッド間のデータ共有は、SharedArrayBufferとAtomicsを使用して実現できます。 SharedArrayBufferを使用すると、メインスレッドとワーカースレッド間でメモリを共有できますが、Atomicsは共有メモリで安全なアトミック操作を実行する方法を提供します。 、フロントエンド開発のためにJavaScriptでマルチスレッドを使用できます。ただし、マルチスレッドを可能にするWebワーカーは、DOMまたは他のWeb APIにアクセスできないことに注意することが重要です。したがって、それらは通常、DOMの操作やWebページとの対話を伴わないタスクに使用されます。

以上がJavaScriptのマルチスレッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。