• Nodejs密集型CPU解决方案


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

    高性能,与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自动退出,这样我们就利用了多进程解决了主线程阻塞的问题。

    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文件内容:

    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主线程的畅通。

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

  • 相关阅读:
    jquery easyui DataGrid
    easyui datagrid使用
    easyui中datagrid用法,加载table数据与标题
    目前已有的框架
    CSS 块级元素、内联元素概念
    设计中最常用的CSS选择器
    ASP.NET MVC 学习
    CSS边框-属性详解
    Boostrap入门级css样式学习
    Codeforces Round #261 (Div. 2)——Pashmak and Graph
  • 原文地址:https://www.cnblogs.com/peiyu1988/p/6700737.html
Copyright © 2020-2023  润新知