Нужны ли мьютексы в javascript?

104

Я видел эту ссылку: Реализация взаимного исключения в JavaScript . С другой стороны, я читал, что в javascript нет потоков, но что именно это означает?

Когда происходят события, где в коде они могут прерываться?

А если в JS нет потоков, нужно ли использовать в JS мьютексы или нет?

В частности, меня интересуют эффекты использования функций, вызываемых setTimeout()и XmlHttpRequestдля onreadystatechangeглобально доступных переменных.

Овеш
источник
1
Нет, нет мьютексов или любого другого инструмента управления параллелизмом в javascript. См. Почему в javascript нет инструмента управления параллелизмом .
Uzair Farooq

Ответы:

101

Javascript определяется как реентерабельный язык, что означает отсутствие потоковой передачи, доступной пользователю, в реализации могут быть потоки. Такие функции, как setTimeout()асинхронные обратные вызовы, должны дождаться, пока обработчик сценариев спит, прежде чем они смогут работать.

Это означает, что все, что происходит в событии, должно быть завершено до того, как будет обработано следующее событие.

При этом вам может понадобиться мьютекс, если ваш код делает что-то там, где он ожидает, что значение не изменится между моментом запуска асинхронного события и моментом вызова обратного вызова.

Например, если у вас есть структура данных, в которой вы нажимаете одну кнопку, и она отправляет XmlHttpRequest, который вызывает обратный вызов, деструктивным образом изменяет структуру данных, и у вас есть другая кнопка, которая напрямую изменяет ту же структуру данных, между тем, когда событие было сработал, и когда был выполнен обратный вызов, пользователь мог щелкнуть и обновить структуру данных перед обратным вызовом, что затем могло бы потерять значение.

Хотя вы можете создать такое состояние гонки, очень легко предотвратить это в своем коде, поскольку каждая функция будет атомарной. Для фактического создания состояния гонки потребовалось бы много работы и несколько странных шаблонов кодирования.

Уильям
источник
14
Создать это состояние гонки совсем несложно: например, у меня есть событие «onkeyup» в поле, которое запускает ajax-вызов БД для получения некоторых значений. Быстрый ввод данных может легко привести к неупорядоченным результатам.
thomasb
19

Ответы на этот вопрос немного устарели, но верны в то время, когда они были даны. И все же правильно, если смотреть на клиентское приложение javascript, которое НЕ использует веб-работников.

Статьи о веб-
воркерах : многопоточность в javascript с использованием веб- воркеров
Mozilla для веб-воркеров

Это ясно показывает, что javascript через веб-воркеров имеет возможности многопоточности. Что касается вопроса, нужны ли мьютексы в javascript? Я не уверен в этом. Но этот пост stackoverflow кажется актуальным:
Взаимное исключение для N асинхронных потоков

гориллатрон
источник
3
взрыв из прошлого, но я столкнулся с необходимостью мьютексов, когда несколько вкладок обращаются к одному и тому же локальному хранилищу
psp
3
WebWorkers не влияют на повторный вход, потому что они не имеют общего состояния переменных и общаются только с основным потоком, передавая сообщения, которые запускают события.
Alnitak
9

Как отмечает @william,

вам может понадобиться мьютекс, если ваш код делает что-то там, где он ожидает, что значение не изменится между моментом запуска асинхронного события и моментом обратного вызова.

Это можно обобщить и дальше - если ваш код делает что-то там, где он ожидает монопольного контроля над ресурсом до тех пор, пока асинхронный запрос не разрешится, вам может понадобиться мьютекс.

Простой пример: у вас есть кнопка, которая запускает вызов ajax для создания записи в серверной части. Вам может потребоваться немного кода, чтобы защитить вас от того, чтобы довольные пользователи щелкали мышью и создавали несколько записей. Есть несколько подходов к этой проблеме (например, отключение кнопки, включение при успешном использовании ajax). Вы также можете использовать простую блокировку:

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

Я не уверен, что это лучший подход, и мне было бы интересно посмотреть, как другие обрабатывают взаимное исключение в javascript, но насколько мне известно, это простой мьютекс, и он удобен.

Alzclarke
источник
4
Я бы вряд ли назвал это мьютексом, по крайней мере, в традиционном смысле, потому что у вас нет двух потоков, работающих в контексте одного блока в любое время.
Ovesh
10
мьютекс - это просто алгоритм, который помогает «избежать одновременного использования общего ресурса». Хотя многопоточность создает потребность в мьютексах, в определении нет ничего, что говорило бы, что мьютекс специфичен для описываемой вами ситуации.
alzclarke
1
Вы правы насчет формального определения мьютекса. Но вряд ли об этом думают люди, когда говорят о мьютексах в реальном мире.
Ovesh
Это не работает должным образом. К сожалению, повторные щелчки по-прежнему вызывают вызов ajax. Есть еще идеи?
Mohammed Shareef C
1
Совершенно уверен, что это должно быть заключено в whileс setTimeoutили setIntervalс clearIntervalпосле n сбоев, чтобы у вас была логика повторных попыток и тайм-аута. Если оставить как есть, вы просто обойдете заблокированный код. Внешняя обработка мьютексов и общих объектов так же важна, как и сами реализации.
MrMesees
6

JavaScript однопоточный ... хотя Chrome может быть новым зверем (я думаю, что он тоже однопоточный, но у каждой вкладки есть собственный поток JavaScript ... Я не рассматривал его подробно, поэтому не цитируйте меня там).

Однако вам НЕОБХОДИМО беспокоиться о том, как ваш JavaScript будет обрабатывать несколько запросов ajax, возвращающихся не в том же порядке, в котором вы их отправляете. Итак, все, о чем вам действительно нужно беспокоиться, - это убедиться, что ваши вызовы ajax обрабатываются таким образом, чтобы они не наступали друг другу на ноги, если результаты вернутся в другом порядке, чем вы их отправили.

Это касается и таймаутов ...

Когда JavaScript становится многопоточным, можно беспокоиться о мьютексах и тому подобном ...

Майк Стоун
источник
4

Да, мьютексы могут потребоваться в Javascript при доступе к ресурсам, которые используются совместно вкладками / окнами, например localStorage .

Например, если у пользователя открыты две вкладки, простой код, подобный следующему, небезопасен:

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

Между моментом, когда элемент localStorage был «получен» и «установлен», другая вкладка могла изменить значение. Как правило, это маловероятно, но возможно - вам нужно будет самостоятельно судить о вероятности и риске, связанных с любым конфликтом в ваших конкретных обстоятельствах.

См. Следующие статьи для получения более подробной информации:

декады
источник
2

Язык JavaScript может быть сколь угодно многопоточным, но при встраивании в браузер движка javascript выполняется только один обратный вызов (onload, onfocus, <script> и т. Д.) За раз (предположительно для каждой вкладки). Предложение Уильяма об использовании Mutex для изменений между регистрацией и получением обратного вызова не следует воспринимать слишком буквально из-за этого, поскольку вы не захотите блокировать промежуточный обратный вызов, поскольку обратный вызов, который его разблокирует, будет заблокирован за текущим обратным вызовом ! (Вау, английский отстой, если говорить о потоковой передаче.) В этом случае вы, вероятно, захотите сделать что-то вроде повторной отправки текущего события, если установлен флаг, буквально или с помощью setTimeout ().

Если вы используете другое встраивание JS и одновременно выполняет несколько потоков, это может стать немного более рискованным, но из-за того, как JS может так легко использовать обратные вызовы и блокировать объекты при доступе к свойствам, явная блокировка не так необходима . Однако я был бы удивлен, если бы встраивание, разработанное для общего кода (например, сценариев игры), в котором использовалась многопоточность, также не давало некоторых явных примитивов блокировки.

Простите за стену текста!

Саймон Бьюкен
источник
0

События сигнализируются, но выполнение JavaScript по-прежнему однопоточное.

Я понимаю, что при получении сигнала о событии движок останавливает то, что он выполняет в данный момент, чтобы запустить обработчик событий. После завершения обработчика выполнение скрипта возобновляется. Если обработчик событий изменил некоторые общие переменные, тогда возобновленный код увидит, что эти изменения появляются «неожиданно».

Если вы хотите «защитить» общие данные, достаточно простого логического флага.

Константин
источник