Okay, bevor wir beginnen, lassen Sie mich sauber kommen und geben zu, dass der Titel dieses Artikels ein kleiner sensationeller ist! JavaScript verfügt über keine Multi-Threading-Funktionen, und es gibt nichts, was ein JavaScript-Programmierer tun kann, um dies zu ändern. In allen Browsern - abgesehen von Google Chrome - wird JavaScript in einem einzigen Ausführungs -Thread ausgeführt, und genau so ist es.
Wir können jedoch Multi-Threading simulieren, sofern es einen der Vorteile einer Umgebung mit mehreren Threads bringt: Es ermöglicht uns, extrem intensiven Code zu betreiben. Dies ist Code, der sonst den Browser einfrieren und eine dieser "nicht reagierenden Skript" -Warnungen in Firefox erzeugen würde.
Es hängt alles auf der Verwendung asynchroner Timer. Wenn wir uns wiederholten Code in einem asynchronen Timer ausführen, geben wir dem Skript -Interpreter des Browsers Zeit, um jede Iteration zu verarbeiten.
Effektiv bittet ein Code -Stück in A für Iterator den Interpreter, alles sofort zu tun: „Führen Sie diesen Code aus n Zeiten so schnell wie möglich aus.“ Der gleiche Code in einem asynchronen Timer bricht jedoch den Code in kleine, diskrete Stücke auf. Das heißt, "Führen Sie diesen Code einmal so schnell wie möglich aus" - dann warten Sie dann "Führen Sie diesen Code einmal so schnell wie möglich aus" usw. n mal.
Der Trick ist, dass der Code in jeder Iteration klein und einfach genug ist, damit der Dolmetscher ihn vollständig innerhalb der Geschwindigkeit des Timers verarbeitet, sei es 100 oder 5.000 Millisekunden. Wenn diese Anforderung erfüllt ist, spielt es keine Rolle, wie intensiv der Gesamtcode ist, da wir nicht darauf bitten, dass er auf einmal ausgeführt wird.
Wenn ich ein Skript schreiben würde, das sich als zu intensiv erwies, würde ich es mir ansehen, es wieder zu entwickeln. Eine so signifikante Verlangsamung zeigt normalerweise ein Problem mit dem Code oder ein tieferes Problem mit der Gestaltung einer Anwendung an.
Aber manchmal nicht. Manchmal gibt es einfach keine Möglichkeit, die Intensität einer bestimmten Operation zu vermeiden, nicht in JavaScript nicht zu tun.
Das könnte die beste Lösung in einem bestimmten Fall sein; Möglicherweise muss eine Verarbeitung in einer Anwendung auf die serverseitige Verschiebung verlegt werden, wo sie im Allgemeinen mehr Verarbeitungsleistung und eine wirklich Thread-Ausführungsumgebung (ein Webserver) enthält.
.Aber irgendwann können Sie eine Situation finden, in der dies einfach keine Option ist - in der JavaScript einfach in der Lage sein muss, etwas zu tun oder verdammt zu sein. Das ist die Situation, in der ich mich bei der Entwicklung meiner Firefox-Erweiterung befand, Dust-Me-Selektoren.
Der Kern dieser Erweiterung ist die Möglichkeit, CSS -Selektoren zu testen, die für eine Seite gelten, um festzustellen, ob sie tatsächlich verwendet werden. Die Essenz davon ist eine Reihe von Bewertungen mit der matchAll () -Methode von Dean Edwards „Base2:
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> }
sicher genug. Aber MatchAll () selbst ist ziemlich intensiv, wenn man - wie es tut -, um einen CSS1- oder CSS2 -Selektor zu analysieren und zu bewerten, und dann den gesamten Dom -Baum zu laufen, der nach Übereinstimmungen sucht. und die Erweiterung tut dies für für jeden einzelnen Selektor , von dem es mehrere Tausend geben kann. Dieser Prozess könnte, auf der Oberfläche so einfach, so intensiv sein, dass der gesamte Browser während der Ereignis einfriert. Und das finden wir.
Das Einspannen des Browsers ist offensichtlich keine Option. Wenn dies überhaupt funktioniert, müssen wir einen Weg finden, es ohne Fehler laufen zu lassen.
zeigen wir das Problem mit einem einfachen Testfall mit zwei Iterationsebenen. Die innere Ebene ist absichtlich zu intensiv, damit wir die Rennbedingungen erstellen können, während die äußere Ebene ziemlich kurz ist, so dass es den Hauptcode simuliert. Das haben wir:
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> }
Wir starten unseren Test und erhalten unsere Ausgabe aus einer einfachen Form (dies ist Testcode, nicht die Produktion. Verzeihen Sie mir also, dass Sie die Verwendung von Inline -Ereignishandlern greifen):
<form action=""> <br> <fieldset> <br> <input type="button" value="test1" onclick="test1()" /> <br> <input type="text" /> <br> </fieldset> <br> </form> <br>
Lassen Sie uns nun diesen Code in Firefox ausführen (in diesem Fall Firefox 3 auf einem 2 -GHz -MacBook)… und wie erwartet friert die Browser -Benutzeroberfläche während des Laufens ein (zum Beispiel unmöglich, den Prozess auf Aktualisieren zu drücken und aufzugeben) . Nach ungefähr 90 Iterationen erzeugt Firefox einen Warndialog „nicht reagierender Skript“.
Wenn wir zulassen, dass es fortgesetzt wird, produziert Firefox nach weiteren 90 Iterationen erneut denselben Dialog.
Safari 3 und Internet Explorer 6 verhalten sich in dieser Hinsicht mit einer gefrorenen Benutzeroberfläche und einem Schwellenwert, an dem ein Warndialog erzeugt wird. In der Opera gibt es keinen solchen Dialog - es führt den Code weiter aus, bis er fertig ist - aber die UI der Browser ist ähnlich eingefroren, bis die Aufgabe abgeschlossen ist.
Eindeutig können wir in der Praxis keinen solchen Code ausführen. Lassen Sie es uns also neu faktorieren und einen asynchronen Timer für die äußere Schleife verwenden:
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> }
Zeigen Sie die Testseite an
(mit der geschäftigen Flag Der Unterprozess läuft gleichzeitig.)Sie sehen, obwohl die Arbeit, die wir am
inneren -Prozess ausführen können Außenschleife im Grunde genommen für immer, und der Browser wird niemals einfrieren. Das ist viel mehr ähnlich - wir können dies in freier Wildbahn verwenden.
Du bist verrückt!Ich kann die Verweigerer bereits hören. Tatsächlich könnte ich selbst einer sein: Warum sollten Sie das tun - was für verrückte Person besteht darauf, JavaScript an all diese Orte zu bringen, für die es nie gedacht war? Ihr Code ist einfach zu intensiv. Dies ist das falsche Werkzeug für den Job. Wenn Sie durch diese Art von Reifen springen müssen, ist das Design Ihrer Bewerbung grundsätzlich falsch.
Aber wenn Sie sind - oder zumindest, wenn Sie offen für überzeugt sind, finden Sie hier ein weiteres Beispiel, das es wirklich nach Hause nagelt: Verwenden Sie JavaScript, um Spiele zu schreiben, in denen Sie gegen den Computer spielen können.
Spiel auf
Was ich hier spreche, ist der Code, der erforderlich ist, um die Regeln eines Spiels zu verstehen, das dann Situationen und Taktiken bewerten kann, um zu versuchen, Sie bei diesem Spiel zu schlagen. Kompliziertes Zeug.
drei Jahre
, von denen der Großteil auf einem Plateau verbracht wurde, in dem das Spiel theoretisch funktionierte, aber zu intensiv war, um es zu verwenden ... bis ich an diesen Ansatz dachte. Das Spiel handelt
Zusammenfassend lässt sich sagen, dass Sie sich mit angrenzenden Form- und Farbanpassungen auf die gesamte Platte begeben. Wenn Sie beispielsweise beispielsweise ein grünes Dreieck beginnen, können Sie zu einem anderen Dreieck oder einer anderen grünen Form wechseln. Ihr Ziel ist es, den Kristall in der Mitte zu erreichen und ihn dann auf die andere Seite des Bretts zu bringen, während Ihr Gegner versucht, dasselbe zu tun. Sie können Ihren Gegner auch den Kristall stehlen.
Wir haben also logische Regeln, die die Bewegung bestimmen, und wir können auch Taktiken auftreten. Um nicht zu vermeiden, dass Ihr Gegner den Kristall erreicht oder ihn stiehlt - können Sie einen Zug auswählen, der ihn blockiert, oder versuchen, an einem Ort zu beenden, den sie nicht erreichen können.
Die Arbeit des Computers besteht darin, den besten Schritt für eine bestimmte Situation zu finden. Schauen wir uns diesen Prozess in Zusammenfängen in der Pseudo-Code an:
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> }
Wir bewerten eine Taktik, und wenn uns das einen guten Schritt gibt, dann sind wir fertig. Ansonsten bewerten wir eine andere Taktik usw., bis wir entweder einen Schritt haben oder zu dem Schluss kommen, dass es keinen gibt und wir bestehen müssen.
Jedes dieser taktischen Funktionen führt einen teuren Prozess aus, da sie jede Position auf dem Board sowie potenzielle zukünftige Positionen bewerten muss, möglicherweise um jeden Male angesichts verschiedener Faktoren. Das Beispiel hat nur drei Taktiken, aber im realen Spiel gibt es Dutzende unterschiedlicher Möglichkeiten, die jeweils teuer zu bewerten.
Eine dieser Bewertungen einzeln ist in Ordnung, aber alle zusammen, führen Sie nacheinander aus, sorgen für einen übermäßig intensiven Prozess, der den Browser einfriert.
Ich habe also den Hauptcode in diskrete -Antasks geteilt, wobei jeder mit einer Switch -Anweisung ausgewählt und mit einem asynchronen Timer iteriert wird. Die Logik davon ist nicht eine Million Meilen von diesen entfernt.
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> }
Aber wir versuchen hier zu tun, ist eine Ausführungsumgebung ohne Decke, dh ein Prozess, der in Bezug auf Komplexität und Länge keine Obergrenze aufweist. Und das haben wir getan.
Dieses Muster kann auf unbestimmte Zeit
mit Hunderten oder sogar Tausenden von Aufgaben erweitert werden. Es mag lange dauern, wenn man läuft, aber es wird ausgeführt werden, und solange jede individuelle Aufgabe nicht zu intensiv ist, wird sie ausgeführt, ohne den Browser zu töten. ein Pfad ohne Rückgabe
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> }
Diese Überprüfung () funktion wird immer falsch zurückgeben, weil die innere Funktion asynchron ist. Die äußere Funktion wird zurückkehren, bevor die erste Iteration der inneren Funktion sogar passiert ist!
Dieses nächste Beispiel ist ähnlich sinnlos:
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> }
Wir sind aus dem Umfang der äußeren Funktion, deshalb können wir nicht davon zurückkehren. Dieser Rückgabewert verschwindet nutzlos in den Äther.
Was wir hier tun können, ist ein Blatt aus Ajax -Codierungstechniken und verwenden Sie eine Rückruffunktion (die in diesem Beispiel "OnComplete" nenne):
<form action=""> <br> <fieldset> <br> <input type="button" value="test1" onclick="test1()" /> <br> <input type="text" /> <br> </fieldset> <br> </form> <br>
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> }
Träumen Androids von Siliziumschafen?
Gehirn , aber es war immer noch zu viel für die konventionelle Iteration; Und es gibt viele andere Spiele da draußen, die viel mehr Einfluss brauchen!
Mein nächster Plan ist es, diese Technik zur Implementierung einer JavaScript -Schachmaschine zu verwenden. Schach hat eine große Auswahl an möglichen Szenarien und Taktiken, was zu Entscheidungen führt, deren Berechnung eine extrem lange Zeit dauern könnte, viel länger als ohne diese Technik machbar gewesen wäre. Eine intensive Berechnung ist erforderlich, um selbst die grundlegendste Denkmaschine zu erstellen, und ich gebe zu, über die Möglichkeiten sehr aufgeregt zu sein.Wenn wir solche Tricks abziehen können, wer soll dann sagen, was möglich ist? Verarbeitung natürlicher Sprache, Heuristiken… Vielleicht haben wir die Bausteine, um künstliche Intelligenz in JavaScript zu entwickeln!
Wenn Sie es genossen haben, diesen Beitrag zu lesen, werden Sie Lernable lieben. Der Ort, um frische Fähigkeiten und Techniken von den Meistern zu erlernen. Mitglieder erhalten sofortigen Zugriff auf alle eBooks und interaktiven Online -Kurse von SitePoint, wie die JavaScript -Programmierung für das Web. Kommentare zu diesem Artikel sind geschlossen. Haben Sie eine Frage zu JavaScript? Warum nicht in unseren Foren fragen? Bildnachweis: Randen L Peterson
Wie kann ich umgehen Datenfreigabe zwischen Threads in JavaScript? SharedArrayBuffer ermöglicht das Teilen des Speichers zwischen dem Haupt-Thread- und Worker-Threads, während Atomics Methoden zur Durchführung sicherer Atomoperationen im gemeinsamen Speicher bietet. Sie können Multithreading in JavaScript für die Front-End-Entwicklung verwenden. Es ist jedoch wichtig zu beachten, dass Webarbeiter, die Multithreading ermöglichen, keinen Zugriff auf die DOM oder andere Web -APIs haben. Daher werden sie in der Regel für Aufgaben verwendet, bei denen das DOM nicht manipuliert oder mit der Webseite interagiert, z. B. Berechnungen oder Handhabungsdaten.
Das obige ist der detaillierte Inhalt vonMulti-Threading in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!