ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScriptのマルチスレッド
わかりました、始める前に、きれいになって、この記事のタイトルが少しセンセーショナルなことを認めましょう! JavaScriptにはマルチスレッド機能が実際にはなく、JavaScriptプログラマーがそれを変更することができることは何もありません。すべてのブラウザで - Google Chromeを除いて - JavaScriptは単一の実行スレッドで実行されます。
しかし、私たちにできることは、マルチスレッド環境の利点の1つを引き起こす限り、マルチスレッドをシミュレートします。これは、それ以外の場合はブラウザをフリーズアップし、Firefoxでそれらの「反応しないスクリプト」警告の1つを生成するコードです。 キーテイクアウト
通常、私が集中的すぎることが証明されたスクリプトを書いている場合、私はそれを再設計することを見ています。このような重要な減速は、通常、コードの問題、またはアプリケーションの設計に関するより深い問題を示しています。
しかし、時にはそうではありません。時々、特定の操作の強度を避ける方法がありません。
それは、特定のケースで最良の解決策かもしれません。おそらく、アプリケーションの一部の処理はサーバー側に移動する必要があります。そこでは、一般的に、そして一般的にスレッドされた実行環境(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を使用してコンピューターと対戦できるゲームを書くことができます。
ゲーム
ここで私が話しているのは、ゲームのルールを理解するために必要なコードです。その後、そのゲームであなたを打ち負かすために状況と戦術を評価できます。複雑なもの。説明するために、私がしばらくの間、私が開発してきたプロジェクトを見ていきます。 「少しの間」とは、
コンピューターの作業は、特定の状況に最適な動きを見つけることです。そのため、そのプロセスを要約の擬似コードに見てみましょう。
戦術を評価します。それが私たちに良い動きを与えた場合、私たちは完了です。それ以外の場合は、別の戦術を評価するか、動きがあるか、それがなくても合格しなければならないと結論付けるまで評価します。
これらの戦術関数のそれぞれは、ボード上のすべての位置と、潜在的な将来の位置を評価する必要があるため、それぞれさまざまな要因に照らして何度も潜在的な将来の位置を評価する必要があるため、高価なプロセスを実行します。この例には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ファイルを指定する必要があります。その後、ポストメッサージメソッドを使用してワーカースレッドと通信し、オンメッセージイベントハンドラーを使用してメッセージからメッセージを受信できます。より速いですが、実行中のタスクの性質に依存します。 CPU集約型タスクの場合、マルチスレッドは、タスクを並行して実行できるようにすることで、パフォーマンスを大幅に改善できます。ただし、I/Oバウンドタスクの場合、これらのタスクはネットワーク速度などのCPUの制御以外の要因によって制限されることが多いため、マルチスレッドの利点はあまり顕著ではありません。 javascriptでのプログラミング?
JavaScriptのスレッド間のデータ共有は、SharedArrayBufferとAtomicsを使用して実現できます。 SharedArrayBufferを使用すると、メインスレッドとワーカースレッド間でメモリを共有できますが、Atomicsは共有メモリで安全なアトミック操作を実行する方法を提供します。 、フロントエンド開発のためにJavaScriptでマルチスレッドを使用できます。ただし、マルチスレッドを可能にするWebワーカーは、DOMまたは他のWeb APIにアクセスできないことに注意することが重要です。したがって、それらは通常、DOMの操作やWebページとの対話を伴わないタスクに使用されます。
以上がJavaScriptのマルチスレッドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。