react platzi node marketing linea cursos curso certificacion carrera avanzado node.js race-condition

platzi - ¿Puede el código node.js resultar en condiciones de carrera?



platzi marketing (6)

Por lo que leo, las condiciones de carrera ocurren cuando diferentes subprocesos intentan cambiar una variable compartida, lo que puede resultar en un valor que no es posible con ningún orden en serie de ejecución de esos subprocesos.

Pero el código en node.js se ejecuta en un solo hilo, entonces, ¿significa que el código escrito en node.js está libre de condiciones de carrera?


Sí. Puede.

La condición de carrera en Nodejs es factible cuando usa el módulo de cluster para inicializar múltiples trabajadores.

El caso

var cluster = require(''cluster''); var fs = require(''fs''); if(cluster.isMaster){ for(var i=0;i<4;i++){ cluster.fork(); } }else{ fs.watch(''/path/to/file'',function(){ var anotherFile = ''/path/to/anotherFile''; fs.readFile(anotherFile,function(er,data){ if(er){ throw er; } data = +data+1; fs.writeFile(anotherFile,data,function(er){ if(er){ throw er; } fs.readFile(anotherFile,function(er,newData){ if(er){ throw er; } console.log(newData); //newData is now undetermined }); }); }); }); }

Cada vez que cambie el archivo visto, 4 trabajadores ejecutarán el controlador al mismo tiempo. Este comportamiento provoca el newData indeterminado.

La solución

if(cluster.isMaster){ var lock = {}; var timer = setInterval(function(){ if(Object.keys(cluster.workers).length >= 4){ return clearInterval(timer); } //note that this lock won''t 100% work if workers are forked at the same time with loop. cluster.fork().on(''message'',function(id){ var isLocked = lock[id]; if(isLocked){ return console.log(''This task has already been handled''); } lock[id] = 1; this.send(''No one has done it yet''); }); },100); }else{ process.on(''message'',function(){ //only one worker can execute this task fs.watch(''/path/to/file'',function(){ var anotherFile = ''/path/to/anotherFile''; fs.readFile(anotherFile,function(er,data){ if(er){ throw er; } data = +data+1; fs.writeFile(anotherFile,data,function(er){ if(er){ throw er; } fs.readFile(anotherFile,function(er,newData){ if(er){ throw er; } console.log(newData); //newData is now determined }); }); }); }); }); //ask the master for permission process.send(''watch''); }


Las condiciones de carrera aún pueden ocurrir, ya que realmente no tienen nada que ver con los hilos, pero al hacer suposiciones sobre el tiempo y la secuencia de eventos, los hilos son solo un ejemplo de eso.

Node.js es de un solo hilo, pero sigue siendo concurrente, y las condiciones de carrera son posibles. Por ejemplo:

var http = require(''http''); var size; http.createServer(function (req, res) { size = 0; req.on(''data'', function (data) { size += data.length; }); req.on(''end'', function () { res.end(size.toString()); }) }).listen(1337, ''127.0.0.1'');

Este programa debe enviar a los clientes un tamaño de su solicitud. Si lo pruebas, parecerá funcionar correctamente. Pero en realidad se basa en el supuesto implícito de que no ocurre nada entre los eventos de inicio y finalización de la solicitud. Si hay 2 o más clientes concurrentes no funcionará.

Esto sucede aquí porque la variable de size se comparte, como cuando dos subprocesos comparten una variable. Puedes pensar en un "contexto asíncrono" abstracto, que se parece mucho a un hilo, pero solo se puede suspender en ciertos puntos.


No. Es cierto que no puede tener una condición de carrera en un solo programa de ejecución de E / S sin subprocesos.

Pero node.js es principalmente rápido debido a su forma de programación no bloqueante. No bloquear significa que al configurar a un oyente para un evento de respuesta, puede hacer otra cosa mientras espera esta respuesta.

Por qué ? Porque el trabajo para obtener la respuesta se realiza en otro hilo. La base de datos, el sistema de archivos, se ejecutan en otro hilo, el cliente obviamente se ejecuta en otra computadora y el flujo de trabajo del programa puede depender de su respuesta.

En términos estrictos, node.js se ejecuta en un subproceso, pero el flujo de trabajo de su programa, que incluye E / S (base de datos, sistema de archivos), cliente y todo, se ejecuta en muchos subprocesos.

Por lo tanto, aún puede haber una condición de carrera si realiza una solicitud para agregar algo a una base de datos, y luego simplemente envía una solicitud para eliminarla sin esperar la respuesta de la primera solicitud. No habría ninguna condición de carrera si la base de datos se estuviera ejecutando en el mismo subproceso que node.js, y la solicitud era solo una llamada a la función ejecutada inmediatamente.


No. Node.js está libre de condiciones de carrera que serían causadas por el cambio de contexto; sin embargo, aún puede escribir un programa node.js donde los eventos asincrónicos que ocurren en un orden inesperado resulten en un estado inconsistente.

Por ejemplo, supongamos que tiene dos funciones. El primero envía un mensaje a través de un WebSocket y, en una devolución de llamada, guarda la respuesta. La segunda función elimina todas las respuestas guardadas. Llamar a las funciones en orden no garantiza una lista de mensajes vacía. Es importante tener en cuenta todos los posibles ordenamientos de eventos al realizar una programación asíncrona.

EDITAR: Aquí hay un código de ejemplo

var messages = []; ... io.sockets.on(''connection'', function (socket) { socket.emit(''ask'', { question: ''How many fish do you have?'' }); socket.on(''reply'', function (data) { messages.push(data); }); ... wipe(); }); function wipe() { setTimeout(function() { messages = []; }, 500); }


Sí. Node.js puede correr en condiciones de carrera tan pronto como comiences a compartir recursos.

También pensé erróneamente que no podía obtener condiciones de carrera en Node.js porque es de naturaleza de un solo subproceso, pero tan pronto como usa un recurso compartido fuera del nodo (por ejemplo, un archivo del sistema de archivos) puede entrar en una condición de carrera. Publiqué un ejemplo de este problema en esta pregunta cuando intentaba entender esto: nodos de lectura de archivo de node.js

Lo que es diferente en Node.js de otros entornos es que tiene un solo hilo de ejecución de JavaScript, por lo que solo hay una instancia de JavaScript ejecutando su código (en oposición a un entorno de subprocesos en el que hay muchos subprocesos que ejecutan su código de aplicación al mismo tiempo). hora.)


, las condiciones de carrera (en el sentido de que un recurso compartido tiene un valor incoherente debido al orden de los eventos) todavía puede ocurrir en cualquier lugar donde haya un punto de suspensión que podría llevar a que se ejecute otro código (con subprocesos en cualquier línea ), tome por ejemplo, este fragmento de código asíncrono que es completamente de un solo hilo:

var accountBalance = 0; async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var balance, newBalance; balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log(''$'' + await getAccountBalance()); // Can print either $50 or $100 // which it prints is dependent on what order // things arrived on the message queue, for this very simple // dummy implementation it actually prints $50 because // all values are added to the message queue immediately // so it actually alternates between the two async functions }; main();

Este código tiene puntos de suspensión en cada espera y, como tal, el contexto podría cambiar entre las dos funciones en un mal momento produciendo "$ 50" en lugar de los "$ 100" esperados, este es esencialmente el mismo ejemplo que el ejemplo de Wikipedia para Condiciones de Carrera en hilos, pero Con puntos explícitos de suspensión / reingreso.

Al igual que los hilos, puedes resolver tales condiciones de carrera con cosas como un bloqueo (también conocido como exclusión mutua). Así podríamos evitar la condición de carrera anterior de la misma manera que los hilos:

var accountBalance = 0; class Lock { constructor() { this._locked = false; this._waiting = []; } lock() { var unlock = () => { var nextResolve; if (this._waiting.length > 0) { nextResolve = this._waiting.pop(0); nextResolve(unlock); } else { this._locked = false; } }; if (this._locked) { return new Promise((resolve) => { this._waiting.push(resolve); }); } else { this._locked = true; return new Promise((resolve) => { resolve(unlock); }); } } } var account = new Lock(); async function getAccountBalance() { // Suppose this was asynchronously from a database or something return accountBalance; }; async function setAccountBalance(value) { // Suppose this was asynchronously from a database or something accountBalance = value; }; async function increment(value, incr) { return value + incr; }; async function add$50() { var unlock, balance, newBalance; unlock = await account.lock(); balance = await getAccountBalance(); newBalance = await increment(balance, 50); await setAccountBalance(newBalance); await unlock(); }; async function main() { var transaction1, transaction2; transaction1 = add$50(); transaction2 = add$50(); await transaction1; await transaction2; console.log(''$'' + await getAccountBalance()); // Now will always be $100 regardless }; main();