在Node.js中并行执行任务
在Node.js中并行化任务的问题出现的原因是JavaScript的回调函数是单线程的,只有I/O是并行化的。为了解决这个问题,可以采取以下方法:
1. 构建无状态的Node服务器,并为每个核心运行一个实例,或者更好地,在每个实例中运行多个虚拟化的微服务器。使用反向代理或负载均衡器来协调传入的请求。
2. 可以将计算任务转移到另一个服务器,比如使用MongoDB(使用MapReduce)或Hadoop。
3. 如果非常专业,可以编写一个C++的Node插件,以精细地控制并行化计算代码。C++的速度可能会使并行化变得不再必要。
4. 可以用最适合数值计算的另一种语言编写代码,并通过REST API公开它们。
5. 最后,根据计算类型(并非所有计算都可以优化为GPU),可以尝试在GPU上运行代码,如node-cuda
或类似的工具。
总之,虽然可以通过fork和spawn生成其他进程,但在我看来,Node的主要优势之一是不需要太担心并行化和线程,从而可以避免许多复杂性。
问题的出现原因是Node.js在单个线程上运行,并且在处理事件循环时一次只能处理一个事件,因此无法实现真正的并行处理。即使在多核机器上运行,也无法在Node.js应用程序内部实现处理的并行性。
解决方法是通过将代码分叉为单独的Node进程或生成子进程,在多核机器上实现处理并行性。这样可以创建多个Node实例,并以不同的方式与这些进程进行通信(例如stdout,进程fork IPC机制)。此外,还可以选择将函数(按责任)分离到自己的Node应用程序/服务器中,并通过RPC调用它。
另外,当使用Node.js时,用于不阻塞调用者的async代码通常使用process.nextTick()。nextTick()会将工作推迟到新的堆栈上,以便可以交错执行CPU密集型任务等。
Node.js没有提供“开箱即用”实现多处理器并发的简单方法。相反,Node.js提供了非阻塞设计和利用线程的事件循环,而不共享内存。多个线程无法共享数据/内存,因此不需要锁定。Node是无锁的。一个Node进程利用一个线程,这使得Node既安全又强大。
当需要将工作分配给多个进程时,可以使用某种消息传递方式与其他进程/服务器进行通信,例如IPC/RPC。
更多信息请参见:
- 关于Node.js的Stack Overflow的精彩回答
“一个利用线程而不共享内存的事件循环”可能会被误解,我不太明确其确切含义。你可以提供一些好的解释的链接吗?
是的,这可能会被误解。谢谢,我会进行澄清。
在Node.js中,异步和并行不是一个概念。异步意味着不需要等待同步。并行意味着可以同时进行多个任务。Node.js只是异步的,但它只有一个线程,只能同时处理一个任务。如果有一个长时间运行的计算任务,应该启动另一个进程,然后让Node.js进程异步等待结果。
为了实现这个目的,可以使用child_process.spawn函数,并从stdin读取数据。
var spawn = require('child_process').spawn; var process2 = spawn('sh', ['./computationProgram', 'parameter'] ); process2.stderr.on('data', function (data) { //处理错误输入 }); process2.stdout.on('data', function (data) { //处理数据结果 });
对的。像Node提供的用于与文件系统交互的函数之类的东西就是通过生成子进程来实现的吗?我认为Node是异步的有它的原因,不只是为了实现并行性。虽然我明白并不是所有的异步操作都是并行的,但有些操作应该是并行的。我的问题是关于哪些操作是并行的,以及它们如何实现“在不阻塞JS执行的情况下做事情”。
Node提供的用于访问文件系统的函数最终通过C与操作系统内核进行交互。在C中,使用read()函数来读取文件,它使用了系统调用#3在Linux内核中。内核并不真正作为一个进程运行,但在这个讨论中可以这样想象。通过使用libc的read()函数和select()函数进行轮询,应用程序可以进行非阻塞读取。我想这就是Node所做的。Node是异步的,这样你的程序就不必等待IO操作。IO操作很慢,而在等待IO的时间里,你的CPU可以处理很多请求。
应该是stdout,而不是stdin,stdin是用于向子进程传递输入的。