36

我有一些我想在 JS 中完成的任务是资源密集型的。对于这个问题,我们假设它们是一些繁重的计算,而不是系统访问。现在我想同时运行任务 A、B 和 C,并在完成后执行一些函数 D。

异步库为此提供了一个很好的脚手架:

async.parallel([A, B, C], D);

如果我正在做的只是计算,那么它仍然会同步运行(除非库本身将任务放在不同的线程上,我希望情况并非如此)。我如何使它实际上是平行的?异步代码通常会做什么来不阻塞调用者(使用 NodeJS 时)?它是否启动了一个子进程

4

5 回答 5

50

我如何使它实际上是平行的?

首先,您不会真正在单节点应用程序中并行运行。节点应用程序在单个线程上运行,节点的事件循环一次只处理一个事件。即使在多核机器上运行,您也不会在节点应用程序中获得并行处理。

也就是说,您可以通过将代码分叉到单独的节点进程通过产生子进程来获得多核机器上的处理并行性。实际上,这允许您创建节点本身的多个实例并以不同的方式与这些进程通信(例如标准输出、进程分叉 IPC 机制)。此外,您可以选择将功能(按职责)分离到它们自己的节点应用程序/服务器中,并通过 RPC 调用它。

异步代码通常会做什么来不阻塞调用者(使用 NodeJS 时)? 它是否启动了一个子进程?

它不是开始一个新的过程。下面,当在 node.js 中使用 async.parallel 时,它正在使用process.nextTick(). nextTick() 允许您通过将工作推迟到新堆栈上来避免阻塞调用者,这样您就可以交错 CPU 密集型任务等。

长话短说

Node 并不容易“开箱即用”地实现多处理器并发。相反,Node 为您提供了一个非阻塞设计和一个利用线程而不共享内存的事件循环。多个线程不能共享数据/内存,因此不需要锁。节点是无锁的。一个节点进程利用一个线程,这使得节点既安全又强大。

当您需要在多个进程之间拆分工作时,请使用某种消息传递与其他进程/服务器进行通信。 例如 IPC/RPC。


有关更多信息,请参阅:

SO关于什么是Node.js的很棒的回答......有很多好处。

了解 process.nextTick()

于 2013-10-01T16:45:13.040 回答
15

异步和并行不是一回事。异步意味着您不必等待同步。并行意味着您可以同时做多项事情。Node.js 只是异步的,但它只有 1 个线程。它一次只能处理一件事。如果你有一个长时间运行的计算,你应该启动另一个进程,然后让你的 node.js 进程异步等待结果。

为此,您可以使用 child_process.spawn,然后从标准输入读取数据。

http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

var spawn = require('child_process').spawn;
var process2 = spawn('sh', ['./computationProgram', 'parameter'] );

process2.stderr.on('data', function (data) {
    //handle error input
});

process2.stdout.on('data', function (data) {
    //handle data results
});
于 2013-10-01T15:47:18.867 回答
6

请记住,I/O 是由 Node.js 并行化的;只有您的 JavaScript 回调是单线程的。

假设您正在编写一个服务器,增加生成进程或分叉的复杂性的另一种方法是简单地构建无状态节点服务器并在每个核心上运行一个实例,或者更好的是在它们自己的虚拟化微服务器中运行多个实例。使用反向代理或负载平衡器协调传入请求。

您还可以将计算卸载到另一台服务器,可能是 MongoDB(使用 MapReduce)或 Hadoop。

要成为真正的硬核,你可以用 C++ 编写一个 Node 插件,并对计算代码的并行化进行细粒度控制。无论如何,C++ 的加速可能会否定并行化的需要。

您始终可以编写代码以使用最适合数字计算的另一种语言执行计算密集型任务,例如通过 REST API 公开它们。

最后,您也许可以使用node-cuda或类似的方式在 GPU 上运行代码,具体取决于计算类型(并非所有都可以针对 GPU 进行优化)。

是的,您可以分叉和生成其他进程,但在我看来,node 的主要优点之一是不必过多担心并行化和线程,因此完全绕过了大量的复杂性。

于 2013-10-01T20:21:45.280 回答
4

根据您的用例,您可以使用类似的东西

task.js用于让 CPU 密集型代码在所有内核(node.js 和 web)上运行的简化接口

一个例子是

function blocking (exampleArgument) {
    // block thread
}

// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);

// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
    // do something with result
});
于 2016-10-28T17:01:44.103 回答
2

最近刚遇到parallel.js,但它似乎实际上是在使用多核并且还具有map reduce 类型的功能。 http://adambom.github.io/parallel.js/

于 2014-01-08T14:30:30.237 回答