javascript concurrency node.js parallel-processing fork-join

javascript - Coordinación de ejecución paralela en node.js



concurrency parallel-processing (7)

Además de las promesas populares y async-library, hay una tercera forma elegante: usar "cableado":

var l = new Wire(); funcA(l.branch(''post'')); funcB(l.branch(''comments'')); funcC(l.branch(''links'')); l.success(function(results) { // result will be object with results: // { post: ..., comments: ..., links: ...} });

https://github.com/garmoshka-mo/mo-wire

El modelo de programación basado en eventos de node.js dificulta la coordinación del flujo del programa.

La ejecución secuencial simple se convierte en devoluciones de llamada anidadas, lo cual es bastante fácil (aunque un poco complicada de anotar).

Pero ¿qué hay de la ejecución en paralelo? Supongamos que tiene tres tareas A, B, C que pueden ejecutarse en paralelo y, cuando finalizan, desea enviar sus resultados a la tarea D.

Con un modelo de tenedor / unión esto sería

  • tenedor A
  • tenedor B
  • tenedor C
  • unirse a A, B, C, correr D

¿Cómo escribo eso en node.js? ¿Hay mejores prácticas o libros de cocina? ¿Tengo que pasar una solución a mano siempre, o hay alguna biblioteca con ayudantes para esto?


Creo que ahora el módulo "asíncrono" proporciona esta funcionalidad paralela y es más o menos la misma que la función de horquilla anterior.


El módulo de futures tiene un submódulo llamado join que me ha gustado usar:

Se une a las llamadas asincrónicas de manera similar a cómo funciona pthread_join para los hilos.

El archivo léame muestra algunos buenos ejemplos del uso de estilo libre o el uso del submódulo future con el patrón Promesa. Ejemplo de los documentos:

var Join = require(''join'') , join = Join() , callbackA = join.add() , callbackB = join.add() , callbackC = join.add(); function abcComplete(aArgs, bArgs, cArgs) { console.log(aArgs[1] + bArgs[1] + cArgs[1]); } setTimeout(function () { callbackA(null, ''Hello''); }, 300); setTimeout(function () { callbackB(null, ''World''); }, 500); setTimeout(function () { callbackC(null, ''!''); }, 400); // this must be called after all join.when(abcComplete);



Nada es verdaderamente paralelo en node.js ya que tiene un solo subproceso. Sin embargo, múltiples eventos se pueden programar y ejecutar en una secuencia que no se puede determinar de antemano. Y algunas cosas, como el acceso a bases de datos, son en realidad "paralelas", ya que las consultas de la base de datos se ejecutan en hilos separados, pero se vuelven a integrar en la secuencia de eventos cuando se completan.

Entonces, ¿cómo programar una devolución de llamada en múltiples controladores de eventos? Bueno, esta es una técnica común utilizada en animaciones en el lado del navegador javascript: use una variable para rastrear la finalización.

Esto suena como un truco y lo es, y parece potencialmente complicado, dejando un montón de variables globales en torno al seguimiento y en un lenguaje menor. Pero en Javascript podemos usar cierres:

function fork (async_calls, shared_callback) { var counter = async_calls.length; var callback = function () { counter --; if (counter == 0) { shared_callback() } } for (var i=0;i<async_calls.length;i++) { async_calls[i](callback); } } // usage: fork([A,B,C],D);

En el ejemplo anterior mantenemos el código simple asumiendo que las funciones asincrónicas y de devolución de llamada no requieren argumentos. Por supuesto, puede modificar el código para pasar argumentos a las funciones asíncronas y hacer que la función de devolución de llamada acumule los resultados y pasarlos a la función shared_callback.

Respuesta adicional:

En realidad, incluso tal como es, esa función fork() ya puede pasar argumentos a las funciones asincrónicas usando un cierre:

fork([ function(callback){ A(1,2,callback) }, function(callback){ B(1,callback) }, function(callback){ C(1,2,callback) } ],D);

lo único que queda por hacer es acumular los resultados de A, B, C y pasarlos a D.

Aún más respuesta adicional:

No pude resistirme. Seguí pensando en esto durante el desayuno. Aquí hay una implementación de fork() que acumula resultados (generalmente pasados ​​como argumentos a la función de devolución de llamada):

function fork (async_calls, shared_callback) { var counter = async_calls.length; var all_results = []; function makeCallback (index) { return function () { counter --; var results = []; // we use the arguments object here because some callbacks // in Node pass in multiple arguments as result. for (var i=0;i<arguments.length;i++) { results.push(arguments[i]); } all_results[index] = results; if (counter == 0) { shared_callback(all_results); } } } for (var i=0;i<async_calls.length;i++) { async_calls[i](makeCallback(i)); } }

Eso fue bastante fácil. Esto hace que fork() tenga un propósito bastante general y se puede usar para sincronizar múltiples eventos no homogéneos.

Ejemplo de uso en Node.js:

// Read 3 files in parallel and process them together: function A (c){ fs.readFile(''file1'',c) }; function B (c){ fs.readFile(''file2'',c) }; function C (c){ fs.readFile(''file3'',c) }; function D (result) { file1data = result[0][1]; file2data = result[1][1]; file3data = result[2][1]; // process the files together here } fork([A,B,C],D);

Actualizar

Este código se escribió antes de la existencia de bibliotecas como async.js o las diversas bibliotecas basadas en promesas. Me gustaría creer que async.js se inspiró en esto, pero no tengo ninguna prueba de ello. De todos modos ... si estás pensando en hacer esto hoy, echa un vistazo a async.js o promesas. Simplemente considere la respuesta anterior sobre una buena explicación / ilustración de cómo funcionan cosas como async.parallel.



Una solución simple podría ser posible aquí: http://howtonode.org/control-flow-part-ii scroll to Parallel actions. Otra forma sería que A, B y C compartan la misma función de devolución de llamada, que la función tenga un incremento global o al menos fuera de la función, si los tres han llamado a la devolución de llamada y luego permiten que se ejecute D, por supuesto, también deberá almacenar los resultados de A, B y C en algún lugar.