Werden Mutexe in Javascript benötigt?

104

Ich habe diesen Link gesehen: Implementieren des gegenseitigen Ausschlusses in JavaScript . Andererseits habe ich gelesen, dass es in Javascript keine Threads gibt, aber was genau bedeutet das?

Wenn Ereignisse auftreten, wo im Code können sie unterbrechen?

Und wenn es in JS keine Threads gibt, muss ich in JS Mutexe verwenden oder nicht?

Insbesondere wundere ich mich über die Auswirkungen der Verwendung von Funktionen, die von setTimeout()und aufgerufen werden XmlHttpRequest, onreadystatechangeauf global zugängliche Variablen.

Ovesh
quelle
1
Nein, kein Mutex oder ein anderes Tool zur Kontrolle der Parallelität in Javascript. Siehe Warum kein Tool zur Kontrolle der Parallelität in Javascript .
Uzair Farooq

Antworten:

101

Javascript ist als wiedereintrittsfähige Sprache definiert. Dies bedeutet, dass dem Benutzer kein Threading zur Verfügung steht und möglicherweise Threads in der Implementierung vorhanden sind. Funktionen wie setTimeout()und asynchrone Rückrufe müssen warten, bis die Skript-Engine in den Ruhezustand versetzt wurde, bevor sie ausgeführt werden können.

Das bedeutet, dass alles, was in einem Ereignis passiert, beendet sein muss, bevor das nächste Ereignis verarbeitet wird.

Abgesehen davon benötigen Sie möglicherweise einen Mutex, wenn Ihr Code etwas tut, bei dem erwartet wird, dass sich ein Wert zwischen dem Auslösen des asynchronen Ereignisses und dem Aufrufen des Rückrufs nicht ändert.

Wenn Sie beispielsweise eine Datenstruktur haben, in der Sie auf eine Schaltfläche klicken und eine XmlHttpRequest sendet, die einen Rückruf aufruft, ändert sich die Datenstruktur destruktiv, und Sie haben eine andere Schaltfläche, die dieselbe Datenstruktur zwischen dem Zeitpunkt des Ereignisses direkt ändert ausgelöst und wenn der Rückruf ausgeführt wurde, hätte der Benutzer vor dem Rückruf auf die Datenstruktur klicken und diese aktualisieren können, wodurch der Wert verloren gehen könnte.

Während Sie eine solche Race-Bedingung erstellen könnten, ist es sehr einfach, dies in Ihrem Code zu verhindern, da jede Funktion atomar ist. Es wäre eine Menge Arbeit und würde einige seltsame Codierungsmuster erfordern, um die Rennbedingung tatsächlich zu erstellen.

Wilhelm
quelle
13
Es ist überhaupt nicht schwer, diese Race-Bedingung zu erstellen: Zum Beispiel habe ich ein "onkeyup" -Ereignis in einem Feld, das einen Ajax-Aufruf an eine DB auslöst, um einige Werte abzurufen. Das schnelle Eingeben von Daten kann leicht zu Ergebnissen führen, die nicht in Ordnung sind.
Thomasb
19

Die Antworten auf diese Frage sind etwas veraltet, obwohl sie zum Zeitpunkt der Beantwortung korrekt waren. Und immer noch richtig, wenn Sie sich eine clientseitige Javascript-Anwendung ansehen, die KEINE Webworker verwendet.

Artikel über Web-Worker:
Multithreading in Javascript mit Webworkern
Mozilla über Webworker

Dies zeigt deutlich, dass Javascript über Web-Worker über Multithreading-Funktionen verfügt. In Bezug auf die Frage werden Mutexe in Javascript benötigt? Da bin ich mir nicht sicher. Dieser Stackoverflow-Beitrag scheint jedoch relevant zu sein:
Gegenseitiger Ausschluss für N asynchrone Threads

Gorillatron
quelle
3
Explosion aus der Vergangenheit, aber ich stieß auf die Notwendigkeit von Mutexen, wenn mehrere Registerkarten auf den gleichen lokalen Speicher
zugreifen
3
WebWorker wirken sich nicht auf die erneute Eingabe aus, da sie keinen variablen Status gemeinsam haben und nur mit dem Hauptthread kommunizieren, indem sie Nachrichten übergeben, die Ereignisse auslösen.
Alnitak
9

Wie @william betont,

Möglicherweise benötigen Sie einen Mutex, wenn Ihr Code etwas tut, bei dem erwartet wird, dass sich ein Wert zwischen dem Auslösen des asynchronen Ereignisses und dem Aufrufen des Rückrufs nicht ändert.

Dies kann weiter verallgemeinert werden. Wenn Ihr Code etwas tut, bei dem er die ausschließliche Kontrolle über eine Ressource erwartet, bis eine asynchrone Anforderung aufgelöst wird, benötigen Sie möglicherweise einen Mutex.

Ein einfaches Beispiel ist eine Schaltfläche, die einen Ajax-Aufruf auslöst, um einen Datensatz im Back-End zu erstellen. Möglicherweise benötigen Sie ein wenig Code, um sich davor zu schützen, dass zufriedene Benutzer wegklicken und dadurch mehrere Datensätze erstellen. Es gibt eine Reihe von Ansätzen für dieses Problem (z. B. Deaktivieren der Schaltfläche, Aktivieren bei Ajax-Erfolg). Sie können auch ein einfaches Schloss verwenden:

var save_lock = false;
$('#save_button').click(function(){
    if(!save_lock){
        //lock
        save_lock=true;
        $.ajax({
            success:function()
                //unlock
                save_lock = false;  
            }
        });
    }
}

Ich bin mir nicht sicher, ob dies der beste Ansatz ist, und ich würde gerne sehen, wie andere mit gegenseitigem Ausschluss in Javascript umgehen, aber soweit ich weiß, ist dies ein einfacher Mutex und praktisch.

alzclarke
quelle
4
Ich würde dies kaum als Mutex bezeichnen, zumindest nicht im herkömmlichen Sinne, da zu keinem Zeitpunkt zwei Threads im Kontext eines einzelnen Blocks ausgeführt werden.
Ovesh
10
Ein Mutex ist einfach ein Algorithmus, der hilft, die gleichzeitige Verwendung einer gemeinsamen Ressource zu vermeiden. Obwohl Multithreading Mutexe erforderlich macht, enthält die Definition nichts, was besagt, dass ein Mutex spezifisch für die von Ihnen beschriebene Situation ist.
Alabclarke
1
Sie haben Recht mit der formalen Definition von Mutex. Aber daran denken die Leute kaum, wenn sie über Mutexe in der realen Welt sprechen.
Ovesh
Dies funktioniert nicht wie erwartet. Leider lösen wiederholte Klicks den Ajax-Aufruf immer noch aus. Irgendeine andere Idee?
Mohammed Shareef C
1
Ziemlich sicher, dass dies in ein whilemit setTimeoutoder ein setIntervalmit clearIntervalnach n Fehlern eingeschlossen werden sollte, damit Sie Wiederholungs- und Zeitüberschreitungslogik haben. Wenn Sie den Status unverändert lassen, umgehen Sie nur den gesperrten Code. Der externe Umgang mit Mutexen und gemeinsam genutzten Objekten ist ebenso wichtig wie die Implementierungen selbst.
MrMesees
6

JavaScript ist Single-Threaded ... obwohl Chrome ein neues Biest sein mag (ich denke, es ist auch Single-Threaded, aber jeder Tab hat seinen eigenen JavaScript-Thread ... Ich habe es nicht im Detail untersucht, also zitiere mich nicht Dort).

Eine Sache, über die Sie sich jedoch Sorgen machen müssen, ist, wie Ihr JavaScript mehrere Ajax-Anfragen verarbeitet, die nicht in derselben Reihenfolge zurückkommen, in der Sie sie senden. Alles, worüber Sie sich wirklich Sorgen machen müssen, ist sicherzustellen, dass Ihre Ajax-Anrufe so behandelt werden, dass sie sich nicht gegenseitig auf die Füße treten, wenn die Ergebnisse in einer anderen Reihenfolge als der von Ihnen gesendeten zurückgegeben werden.

Dies gilt auch für Auszeiten ...

Wenn JavaScript Multithreading erweitert, sollten Sie sich möglicherweise Gedanken über Mutexe und dergleichen machen.

Mike Stone
quelle
4

Ja, in Javascript können Mutexe erforderlich sein, wenn auf Ressourcen zugegriffen wird , die von Registerkarten / Fenstern gemeinsam genutzt werden, z . B. localStorage .

Wenn ein Benutzer beispielsweise zwei Registerkarten geöffnet hat, ist einfacher Code wie der folgende unsicher:

function appendToList(item) {
    var list = localStorage["myKey"];
    if (list) {
        list += "," + item;
    }
    else {
        list = item;
    }
    localStorage["myKey"] = list;
}

Zwischen dem Zeitpunkt, an dem das localStorage-Element "got" und "set" ist, könnte eine andere Registerkarte den Wert geändert haben. Es ist im Allgemeinen unwahrscheinlich, aber möglich - Sie müssen die Wahrscheinlichkeit und das Risiko eines Konflikts unter Ihren besonderen Umständen selbst beurteilen.

Weitere Informationen finden Sie in den folgenden Artikeln:

entscheidet
quelle
2

JavaScript, die Sprache , kann beliebig multithreaded sein, aber Browser-Einbettungen der Javascript-Engine führen jeweils nur einen Rückruf (Onload, Onfocus, <Script> usw.) aus (vermutlich pro Registerkarte). Williams Vorschlag, einen Mutex für Änderungen zwischen dem Registrieren und dem Empfangen eines Rückrufs zu verwenden, sollte aus diesem Grund nicht zu wörtlich genommen werden, da Sie den dazwischenliegenden Rückruf nicht blockieren möchten, da der Rückruf, der ihn entsperrt, hinter dem aktuellen Rückruf blockiert wird ! (Wow, Englisch ist scheiße, wenn man über Threading spricht.) In diesem Fall möchten Sie wahrscheinlich etwas tun, um das aktuelle Ereignis erneut zu verteilen, wenn ein Flag gesetzt ist, entweder buchstäblich oder mit setTimeout ().

Wenn Sie eine andere Einbettung von JS verwenden und mehrere Threads gleichzeitig ausführen, kann dies etwas schwieriger werden. Aufgrund der Art und Weise, wie JS Rückrufe so einfach verwenden kann und Objekte beim Zugriff auf Eigenschaften sperrt, ist eine explizite Sperrung bei weitem nicht erforderlich . Es würde mich jedoch wundern, wenn eine Einbettung für allgemeinen Code (z. B. Game Scripting), die Multithreading verwendet, nicht auch einige explizite Sperrprimitive enthält.

Entschuldigung für die Textwand!

Simon Buchan
quelle
0

Ereignisse werden signalisiert, aber die JavaScript-Ausführung ist immer noch Single-Threaded.

Nach meinem Verständnis stoppt die Engine, wenn ein Ereignis signalisiert wird, was sie gerade ausführt, um den Ereignishandler auszuführen. Nach Abschluss des Handlers wird die Skriptausführung fortgesetzt. Wenn der Ereignishandler einige gemeinsam genutzte Variablen geändert hat, werden diese Änderungen im wieder aufgenommenen Code "aus heiterem Himmel" angezeigt.

Wenn Sie gemeinsam genutzte Daten "schützen" möchten, sollte ein einfaches boolesches Flag ausreichen.

Constantin
quelle