Czy muteksy są potrzebne w javascript?


104

Widziałem ten link: Implementowanie wzajemnego wykluczania w JavaScript . Z drugiej strony przeczytałem, że w javascript nie ma wątków, ale co to dokładnie oznacza?

Kiedy występują zdarzenia, gdzie w kodzie mogą one przerwać?

A jeśli w JS nie ma wątków, czy muszę używać muteksów w JS, czy nie?

Konkretnie zastanawiam się o skutkach korzystania z funkcji zwanych przez setTimeout()a XmlHttpRequest„s onreadystatechangeod zmiennych dostępnych globalnie.


1
Nie, nie ma muteksu ani żadnego innego narzędzia do kontroli współbieżności w javascript. Zobacz, dlaczego nie ma narzędzia do kontroli współbieżności w języku JavaScript .
Uzair Farooq

Odpowiedzi:


101

Javascript jest zdefiniowany jako język ponownie wprowadzany , co oznacza, że ​​użytkownik nie ma żadnych wątków, a implementacja może zawierać wątki. Funkcje takie jak setTimeout()i asynchroniczne wywołania zwrotne muszą czekać na uśpienie silnika skryptów, zanim będą mogły zostać uruchomione.

Oznacza to, że wszystko, co dzieje się w wydarzeniu, musi zostać zakończone, zanim następne zdarzenie zostanie przetworzone.

Biorąc to pod uwagę, możesz potrzebować muteksu, jeśli kod robi coś, w którym oczekuje, że wartość nie zmieni się między momentem uruchomienia zdarzenia asynchronicznego a wywołaniem wywołania zwrotnego.

Na przykład, jeśli masz strukturę danych, w której klikasz jeden przycisk i wysyła ona XmlHttpRequest, która wywołuje wywołanie zwrotne, zmienia strukturę danych w destrukcyjny sposób, i masz inny przycisk, który zmienia tę samą strukturę danych bezpośrednio, między momentem, w którym zdarzenie zostało uruchomione, a gdy wywołanie zwrotne zostało wykonane, użytkownik mógł kliknąć i zaktualizować strukturę danych przed wywołaniem zwrotnym, co może spowodować utratę wartości.

Chociaż możesz stworzyć taki stan wyścigu, bardzo łatwo jest temu zapobiec w swoim kodzie, ponieważ każda funkcja będzie atomowa. Wymagałoby to dużo pracy i wymagałoby kilku dziwnych wzorców kodowania, aby w rzeczywistości stworzyć warunki wyścigu.


14
Utworzenie tego warunku wyścigu nie jest wcale trudne: na przykład mam zdarzenie „onkeyup” w polu, które wyzwala wywołanie ajax do bazy danych w celu pobrania pewnych wartości. Szybkie wpisywanie danych może łatwo prowadzić do nieprawidłowych wyników.
thomasb

19

Odpowiedzi na to pytanie są nieco nieaktualne, choć poprawne w momencie ich udzielenia. I nadal poprawne, jeśli patrzysz na aplikację javascript po stronie klienta, która NIE korzysta z webworkerów.

Artykuły o pracownikach internetowych:
wielowątkowość w javascript przy użyciu webworkers
Mozilla na webworkersach

To wyraźnie pokazuje, że javascript za pośrednictwem pracowników sieci ma możliwości wielowątkowości. Jeśli chodzi o pytanie, czy muteksy są potrzebne w javascript? Nie jestem tego pewien. Ale ten post dotyczący przepływu stosów wydaje się mieć znaczenie:
Wzajemne wykluczanie N wątków asynchronicznych


3
wybuch z przeszłości, ale napotkałem potrzebę muteksów, gdy wiele kart uzyskuje dostęp do tego samego lokalnego magazynu
psp

3
WebWorkerzy nie wpływają na ponowne wejście, ponieważ nie mają wspólnego stanu zmiennej i komunikują się z głównym wątkiem tylko poprzez przekazywanie komunikatów, które wyzwalają zdarzenia.
Alnitak

9

Jak wskazuje @william,

mutex może być potrzebny, jeśli Twój kod robi coś tam, gdzie oczekuje, że wartość nie zmieni się między momentem uruchomienia zdarzenia asynchronicznego a wywołaniem wywołania zwrotnego.

Można to uogólnić dalej - jeśli twój kod robi coś, w którym oczekuje wyłącznej kontroli nad zasobem do czasu rozwiązania asynchronicznego żądania, możesz potrzebować muteksu.

Prostym przykładem jest przycisk, który odpala wywołanie Ajax w celu utworzenia rekordu w zapleczu. Możesz potrzebować trochę kodu, aby uchronić Cię przed wywołaniem kliknięcia przez zadowolonych użytkowników, a tym samym utworzenia wielu rekordów. istnieje kilka podejść do tego problemu (np. wyłącz przycisk, włącz przy sukcesie Ajax). Możesz także użyć prostego zamka:

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

Nie jestem pewien, czy to najlepsze podejście i chciałbym zobaczyć, jak inni radzą sobie z wzajemnym wykluczaniem w javascript, ale o ile wiem, jest to prosty muteks i jest przydatny.


4
Nie nazwałbym tego muteksem, przynajmniej nie w tradycyjnym sensie, ponieważ w żadnym momencie nie masz dwóch wątków działających w kontekście pojedynczego bloku.
Ovesh,

10
mutex to po prostu algorytm, który pomaga „uniknąć jednoczesnego wykorzystania wspólnego zasobu”. Chociaż wielowątkowość stwarza potrzebę stosowania muteksów, w definicji nie ma nic, co mówi, że mutex jest specyficzny dla opisywanej sytuacji.
alzclarke

1
Masz rację co do formalnej definicji muteksu. Ale nie o tym myślą ludzie, gdy mówią o muteksach w prawdziwym świecie.
Ovesh,

To nie działa zgodnie z oczekiwaniami. Niestety, powtarzające się kliknięcia nadal będą wywoływać wywołanie Ajax. Masz inny pomysł?
Mohammed Shareef C

1
Całkiem pewny, że powinno to być opakowane a whilewith setTimeoutlub a setIntervalwith clearIntervalafter n nieudanych, aby mieć logikę ponawiania i limitu czasu. Pozostawienie tak, jak jest, oznacza, że ​​po prostu ominiesz kod, który jest zablokowany. Zewnętrzna obsługa muteksów i obiektów współdzielonych jest równie ważna, jak same implementacje.
MrMesees

6

JavaScript jest jednowątkowy ... chociaż Chrome może być nową bestią (myślę, że jest również jednowątkowy, ale każda karta ma swój własny wątek JavaScript ... Nie zaglądałem do tego szczegółowo, więc nie cytuj mnie tam).

Jednak jedną rzeczą, o którą musisz się martwić, jest to, w jaki sposób Twój JavaScript obsłuży wiele żądań Ajax wracających w innej kolejności, w jakiej je wysyłasz. Tak więc, wszystko, o co naprawdę musisz się martwić, to upewnić się, że twoje wywołania Ajax są obsługiwane w taki sposób, że nie nadepną sobie nawzajem na nogi, jeśli wyniki pojawią się w innej kolejności niż je wysłałeś.

Dotyczy to również limitów czasu ...

Kiedy JavaScript staje się wielowątkowy, może martwić się o muteksy i tym podobne ...


4

Tak, muteksy mogą być wymagane w JavaScript podczas uzyskiwania dostępu do zasobów współdzielonych między kartami / oknami, takich jak localStorage .

Na przykład, jeśli użytkownik ma otwarte dwie karty, prosty kod, taki jak poniższy, jest niebezpieczny:

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

Od czasu, gdy element localStorage jest „got” i „set”, inna karta mogłaby zmodyfikować wartość. Generalnie jest to mało prawdopodobne, ale możliwe - musiałbyś samodzielnie ocenić prawdopodobieństwo i ryzyko związane z jakimkolwiek sporem w określonych okolicznościach.

Więcej informacji znajdziesz w następujących artykułach:


2

JavaScript, język , może być tak wielowątkowy, jak chcesz, ale osadzanie przeglądarki silnika javascript uruchamia tylko jedno wywołanie zwrotne (onload, onfocus, <script> itp.) Na raz (przypuszczalnie na kartę). Sugestia Williama dotycząca używania Mutexa do zmian między rejestracją a otrzymaniem oddzwonienia nie powinna być traktowana zbyt dosłownie z tego powodu, ponieważ nie chciałbyś blokować w interweniującym wywołaniu zwrotnym, ponieważ wywołanie zwrotne, które je odblokuje, zostanie zablokowane za bieżącym wywołaniem zwrotnym ! (Wow, angielski jest do niczego, jeśli chodzi o rozmawianie o wątkach.) W tym przypadku prawdopodobnie chcesz zrobić coś na wzór ponownego wysłania bieżącego zdarzenia, jeśli flaga jest ustawiona, dosłownie lub za pomocą funkcji setTimeout ().

Jeśli używasz innego osadzania JS i wykonuje on wiele wątków jednocześnie, może to być nieco bardziej ryzykowne, ale ze względu na sposób, w jaki JS może tak łatwo używać wywołań zwrotnych i blokować obiekty w dostępie do właściwości, jawne blokowanie nie jest tak potrzebne . Byłbym jednak zaskoczony, gdyby osadzanie zaprojektowane dla ogólnego kodu (np. Skryptów gier), które wykorzystywało wielowątkowość, nie dawało również wyraźnych prymitywów blokujących.

Przepraszam za ścianę tekstu!


0

Zdarzenia są sygnalizowane, ale wykonanie JavaScript jest nadal jednowątkowe.

Rozumiem, że po zasygnalizowaniu zdarzenia silnik zatrzymuje to, co obecnie wykonuje, aby uruchomić procedurę obsługi zdarzenia. Po zakończeniu obsługi, wykonywanie skryptu jest wznawiane. Jeśli program obsługi zdarzeń zmienił niektóre współdzielone zmienne, wznowiony kod spowoduje, że te zmiany pojawią się „nieoczekiwanie”.

Jeśli chcesz "chronić" współdzielone dane, wystarczy prosta flaga boolowska.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.