nodejs 如何处理密集型计算


本文摘自PHP中文网,作者藏色散人,侵删。

nodejs 如何处理密集型计算

Nodejs密集型CPU解决方案

首先说一下nodejs单线程的优势:

推荐:《javascript高级教程

高性能,与php相比,避免了频繁创建切换线程的开销,执行更加迅速,资源占用小。

线程安全,不用担心同一变量被多线程读写,造成程序崩溃。

单线程的异步和非阻塞,其实 nodejs底层访问I/O还是多线程的,阻塞/非阻塞与异步/同步是两个不同的概念,同步不代表阻塞,但是阻塞肯定就是同步;有点儿绕口,请听我举例,我去食堂打饭,我选择了A套餐,然后工作人员帮我去配餐,如果我就站在旁边,等待工作人员给我配餐,这种情况就称之为同步;若工作人员帮我配餐的同时,排在我后面的人就开始点餐,这样整个食堂的点餐服务并没有因为我在等待A套餐而停止,这种情况就称之为非阻塞。这个例子就简单说明了同步但非阻塞的情况。再如果我在等待配餐的时候去买饮料,等听到叫号再回去拿套餐,此时我的饮料也已经买好,这样我在等待配餐的同时还执行了买饮料的任务,叫号就等于执行了回调,就是异步非阻塞了。如果我在买饮料的时候,已经叫我的号让我去拿套餐,可是我等了好久才拿到饮料,所以我可能在大厅叫我的餐号之后很久才拿到A套餐,这也就是单线程的阻塞情况。

多线程:

线程是cpu调度的一个基本单位,一个cpu只能执行一个线程任务。

nodejs也可以执行多行程任务,例如引用TAGG/TAGG2模块,但是不论是tagg/tagg2都是利用pthread库和V8::Isolate类来实现js多线程功能的,根据规则我们在线程里执行的函数无法使用nodejs的核心api,例如fs,crypto模块,所以还是有很大的局限性。

多进程:

在支持html5的浏览器里,我们可以使用webworker来将一些耗时的计算丢入worker进程中执行,这样主进程就不会阻塞,用户也不会有卡顿的感觉。

这里我们需要利用nodejs的child_process模块,child_process提供了fork方法,可以启动一个nodejs文件,将它视作worker进程,worker 工作完毕后,会把结果send给主进程,然后worker自动退出,这样我们就利用了多进程解决了主线程阻塞的问题。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

var express = require('express');

var fork = require('child_process').fork;

var app = express();

app.get('/', function(req, res){

  var worker = fork('./work.js') //创建一个工作进程

  worker.on('message', function(m) {//接收工作进程计算结果

          if('object' === typeof m && m.type === 'fibo'){

                   worker.kill();//发送杀死进程的信号

                   res.send(m.result.toString());//将结果返回客户端

          }

  });

  worker.send({type:'fibo',num:~~req.query.n || 1});

  //发送给工作进程计算fibo的数量

});

app.listen(7878);

我们通过express监听7878端口,对每个用户的请求都会去fork一个子进程,通过调用worker.send方法将参数n传递给子进程,同时监听子进程发送消息的message事件,将结果响应给客户端。

下面是被fork的work.js文件内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

var fibo = function fibo (n) {//定义算法

   return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;

}

process.on('message', function(m) {

//接收主进程发送过来的消息

          if(typeof m === 'object' && m.type === 'fibo'){

                  var num = fibo(~~m.num);

                  //计算jibo

                  process.send({type: 'fibo',result:num})

                  //计算完毕返回结果      

          }

});

process.on('SIGHUP', function() {

        process.exit();//收到kill信息,进程退出

});

我们先定义函数fibo用来计算斐波那契数组,然后监听了主线程发来的消息,计算完毕之后将结果send到主线程。同时还监听process的SIGHUP事件,触发此事件就进程退出。

这里我们有一点需要注意,主线程的kill方法并不是真的使子进程退出,而是会触发子进程的SIGHUP事件,真正的退出还是依靠process.exit()。

总结:

使用child_process模块的fork方法确实可以让我们很好的解决单线程对cpu密集型任务的阻塞问题,同时又没有tagg包那样无法使用Node.js核心api的限制。

单线程异步的Node.js不代表不会阻塞,在主线程做过多的任务可能会导致主线程的卡死,影响整个程序的性能,所以我们要非常小心的处理大量的循环,字符串拼接和浮点运算等cpu密集型任务,合理的利用各种技术把任务丢给子线程或子进程去完成,保持Node.js主线程的畅通。

线程/进程的使用并不是没有开销的,尽可能减少创建和销毁线程/进程的次数,可以提升我们系统整体的性能和出错的概率。

以上就是nodejs 如何处理密集型计算的详细内容,更多文章请关注木庄网络博客

相关阅读 >>

浅谈conda安装nodejs版本过低问题的解决方法

解决包管理工具 npm 和 yarn 下载安装超时的方法

nodejs的爬虫框架superagent

详解nodejs中的querystring模块

如何查看nodejs是否安装成功

如何使用nodejs实现路由功能

html5+nodejs实现websocket即时通讯的示例代码分享

nodejs是js框架么?

聊聊nodejs中的模块化和事件循环

node.js访问sql数据库的方法

更多相关阅读请进入《nodejs》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...