¿Se necesitan mutexes en javascript?

104

He visto este enlace: Implementación de la exclusión mutua en JavaScript . Por otro lado, he leído que no hay hilos en javascript, pero ¿qué significa eso exactamente?

Cuando ocurren eventos, ¿en qué parte del código pueden interrumpir?

Y si no hay subprocesos en JS, ¿necesito usar mutex en JS o no?

En concreto, me pregunto acerca de los efectos del uso de las funciones llamadas por setTimeout()y XmlHttpRequest's onreadystatechangeen variables globalmente accesibles.

Ovesh
fuente
1
No, sin mutex o cualquier otra herramienta de control de concurrencia en javascript. Consulte Por qué no hay una herramienta de control de concurrencia en JavaScript .
Uzair Farooq

Respuestas:

101

Javascript se define como un lenguaje reentrante , lo que significa que no hay subprocesos expuestos al usuario, puede haber subprocesos en la implementación. Las funciones como setTimeout()las devoluciones de llamada asincrónicas deben esperar a que el motor de secuencia de comandos se suspenda antes de poder ejecutarse.

Eso significa que todo lo que sucede en un evento debe terminarse antes de que se procese el próximo evento.

Dicho esto, es posible que necesite un mutex si su código hace algo en el que espera que un valor no cambie entre el momento en que se disparó el evento asíncrono y el momento en que se llamó a la devolución de llamada.

Por ejemplo, si tiene una estructura de datos en la que hace clic en un botón y envía una XmlHttpRequest que llama a una devolución de llamada, cambia la estructura de datos de una manera destructiva, y tiene otro botón que cambia la misma estructura de datos directamente, entre el momento en que se produjo el evento. se disparó y cuando se ejecutó la devolución de llamada, el usuario pudo haber hecho clic y actualizado la estructura de datos antes de la devolución de llamada, lo que podría perder el valor.

Si bien puede crear una condición de carrera como esa, es muy fácil evitarlo en su código, ya que cada función será atómica. Sería mucho trabajo y se necesitarían algunos patrones de codificación extraños para crear la condición de carrera de hecho.

Guillermo
fuente
13
No es nada difícil crear esta condición de carrera: por ejemplo, tengo un evento "onkeyup" en un campo, que activa una llamada ajax a una base de datos para obtener algunos valores. Escribir datos rápidamente puede conducir fácilmente a resultados desordenados.
thomasb
19

Las respuestas a esta pregunta están un poco desactualizadas, aunque son correctas en el momento en que se dieron. Y aún así es correcto si mira una aplicación de JavaScript del lado del cliente que NO usa webworkers.

Artículos sobre trabajadores web:
multiproceso en javascript usando webworkers
Mozilla en webworkers

Esto muestra claramente que javascript a través de web-workers tiene capacidades de subprocesos múltiples. En cuanto a la pregunta, ¿se necesitan mutex en javascript? No estoy seguro de esto. Pero esta publicación de stackoverflow parece relevante:
Exclusión mutua para N subprocesos asincrónicos

gorillatron
fuente
3
explosión del pasado, pero encontré la necesidad de mutex cuando varias pestañas acceden al mismo almacenamiento local
psp
3
Los WebWorkers no afectan la reentrada porque no comparten un estado variable y solo se comunican con el hilo principal pasando mensajes, que desencadenan eventos.
Alnitak
9

Como señala @william,

es posible que necesite un mutex si su código hace algo donde espera que un valor no cambie entre el momento en que se disparó el evento asíncrono y el momento en que se llamó a la devolución de llamada.

Esto se puede generalizar aún más: si su código hace algo en lo que espera el control exclusivo de un recurso hasta que se resuelve una solicitud asincrónica, es posible que necesite un mutex.

Un ejemplo simple es donde tiene un botón que dispara una llamada ajax para crear un registro en el back-end. Es posible que necesite un poco de código para evitar que los usuarios felices hagan clic y creen múltiples registros. Hay una serie de enfoques para este problema (por ejemplo, deshabilitar el botón, habilitar en caso de éxito de ajax). También puede usar un candado simple:

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

No estoy seguro de si ese es el mejor enfoque y me interesaría ver cómo otros manejan la exclusión mutua en javascript, pero hasta donde sé, es un simple mutex y es útil.

Alzclarke
fuente
4
Difícilmente llamaría a esto un mutex, al menos no en el sentido tradicional, porque no tienes dos subprocesos ejecutándose en el contexto de un solo bloque en ningún momento.
Ovesh
10
un mutex es simplemente un algoritmo que ayuda a "evitar el uso simultáneo de un recurso común". Aunque el multiproceso crea la necesidad de mutex, no hay nada en la definición que diga que un mutex es específico para la situación que describe.
alzclarke
1
Tiene razón sobre la definición formal de mutex. Pero esto no es lo que la gente piensa cuando habla de mutex en el mundo real.
Ovesh
Esto no funciona como se esperaba. Desafortunadamente, los clics repetidos aún activarán la llamada ajax. ¿Alguna otra idea?
Mohammed Shareef C
1
Estoy bastante seguro de que esto debería estar envuelto en un whilecon setTimeouto setIntervalcon clearIntervaldespués de n fallas para que tenga una lógica de reintento y tiempo de espera. Dejarlo como está significa que simplemente omitiría el código que está bloqueado. El manejo externo con mutex y objetos compartidos es tan importante como las implementaciones mismas.
MrMesees
6

JavaScript es de un solo subproceso ... aunque Chrome puede ser una nueva bestia (creo que también es de un solo subproceso, pero cada pestaña tiene su propio hilo de JavaScript ... No lo he analizado en detalle, así que no me cites ahí).

Sin embargo, una cosa de la que DEBE preocuparse es cómo su JavaScript manejará múltiples solicitudes ajax que regresan en el mismo orden en que las envía. Por lo tanto, todo lo que realmente debe preocuparse es asegurarse de que sus llamadas ajax se manejen de una manera que no se pisoteen si los resultados regresan en un orden diferente al que los envió.

Esto también se aplica a los tiempos de espera ...

Cuando JavaScript crece con múltiples subprocesos, entonces tal vez se preocupe por las exclusiones mutuas y similares ...

Mike Stone
fuente
4

Sí, se pueden requerir exclusiones mutuas en Javascript al acceder a recursos que se comparten entre pestañas / ventanas, como localStorage .

Por ejemplo, si un usuario tiene dos pestañas abiertas, un código simple como el siguiente no es seguro:

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

Entre el momento en que el elemento localStorage se 'obtiene' y 'establece', otra pestaña podría haber modificado el valor. Por lo general, es poco probable, pero es posible: deberá juzgar por sí mismo la probabilidad y el riesgo asociados con cualquier disputa en sus circunstancias particulares.

Consulte los siguientes artículos para obtener más detalles:

decae
fuente
2

JavaScript, el lenguaje , puede ser tan multiproceso como desee, pero las incrustaciones del navegador del motor javascript solo ejecutan una devolución de llamada (onload, onfocus, <script>, etc.) a la vez (por pestaña, presumiblemente). La sugerencia de William de usar un mutex para los cambios entre el registro y la recepción de una devolución de llamada no debe tomarse demasiado literalmente debido a esto, ya que no querrá bloquear la devolución de llamada intermedia ya que la devolución de llamada que lo desbloqueará se bloqueará detrás de la devolución de llamada actual. ! (Vaya, el inglés apesta para hablar de subprocesos.) En este caso, probablemente quieras hacer algo en la línea de volver a enviar el evento actual si se establece una bandera, ya sea literalmente o con cosas como setTimeout ().

Si está utilizando una incrustación diferente de JS, y eso ejecuta varios subprocesos a la vez, puede ser un poco más arriesgado, pero debido a la forma en que JS puede usar devoluciones de llamada tan fácilmente y bloquea objetos en el acceso a la propiedad, el bloqueo explícito no es tan necesario . Sin embargo, me sorprendería que una incrustación diseñada para código general (p. Ej., Secuencias de comandos de juegos) que usara múltiples subprocesos no proporcionara también algunas primitivas de bloqueo explícitas.

¡Perdón por la pared de texto!

Simon Buchan
fuente
0

Los eventos se señalan, pero la ejecución de JavaScript sigue siendo de un solo subproceso.

Tengo entendido que cuando se indica un evento, el motor detiene lo que está ejecutando en ese momento para ejecutar el controlador de eventos. Una vez finalizado el controlador, se reanuda la ejecución del script. Si el controlador de eventos cambió algunas variables compartidas, el código reanudado verá estos cambios aparecer "de la nada".

Si desea "proteger" los datos compartidos, una simple bandera booleana debería ser suficiente.

Constantin
fuente