首页 > 编程语言 >nodejs多线程,真正的非阻塞

nodejs多线程,真正的非阻塞

时间:2023-05-21 13:06:09浏览次数:51  
标签:node function console nodejs 阻塞 40 fibo 线程 多线程


干货:收藏: http://cnodejs.org/topic/518b679763e9f8a5424406e9

node从他推出至今,充满赞美和饱受诟病的都是其单线程模型,所有的任务都在一个线程中完成(I/O等例外),优势的地方自然是免去了频繁切换线程的开销,以及减少资源互抢的问题等等,但是当nodejs面对cpu密集型模型的时候就力不从心了。尽管node拥有异步机制,可以把一些耗时算法丢入eventloop等待下个事件循环再做,但是因为其任然是单线程模型,所以终究会造成阻塞。

先解释一下两个名词,Fibers 和 Threads。
Fibers 又称纤程,可以理解为协同程序,类似py和lua都有这样的模型。使用Fibers可以避免对资源的互抢,减少cpu和内存的消耗,但是Fibers并不能够真正的并行执行,同一时刻只有一个Fibers在执行,如果在其中一个Fibers中执行过多的cpu操作或者写了个死循环,则整个主程序将卡死住。node中的异步事件循环模型就有点象这个。

Threads 又称线程,他可以在同一时刻并行的执行,他们共享主进程的内存,在其中某一时刻某一个threads锁死了,是不会影响主线程以及其他线程的执行。但是为了实现这个模型,我们不得不消耗更多的内存和cpu为线程切换的开销,同时也存在可能多个线程对同一内存单元进行读写而造成程序崩溃的问题。

很多让node支持多线程的方法是使用c/c++的addon来实现,在需要进行cpu密集型计算的地方,把js代码改写成c/c++代码,但是如果开发人员对c++不是很熟悉,一来开发效率会降低不少,二来也容易出bug,而且我们知道在addon中的c++代码除了编译出错外,是很难调试的,毕竟没有vs调试c++代码方便。

令人振奋的消息,我们为什么不让node也支持多线程模型呢?于是Jorge为我们开发出了一个让node支持多线程模型的模块:threads_a_gogo
github地址:https://github.com/xk/node-threads-a-gogo

有了threads-a-gogo(以下简称TAGG)这个模块之后,我们可以让node做更多的事情,我记得以前我看过一篇文章,说node只能应付i/o密集型场景,在cpu密集型场景将完败给apache,因为apache是为每一个请求起一条线程的,所以在处理cpu密集型任务时一个线程的高强度计算不会很大程度的影响其他线程,类似的还有php的fastcgi,这也是很多拿node和php进行比较时,php的拥护者们一直提出的理论。

我们先来做一个简单的测试,用我们suqian大大最喜欢的斐波那契数组来看一下,加入了多线程的node有多么的强悍:(测试机器为4CPU)
没有使用TAGG的正常情况,异步也帮不了我们应对cpu密集型任务

function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}var n=8function back(){if(!--n)return console.timeEnd('no thread');}
    console.time('no thread');

    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})

    process.nextTick(function(){
        console.log(fibo (40));
        back();})

process.nextTick(function(){
    console.log(fibo (40));
    back();})
process.nextTick(function(){
    console.log(fibo (40));
    back();})
process.nextTick(function(){
    console.log(fibo (40));
    back();})

我们模拟了8个异步的行为,测试用的node v0.8.16版本,所以 process.nextTick还是异步方法。最后我们输出结果为:

165580141165580141165580141165580141165580141165580141165580141165580141no thread:23346ms

接下来我们使用TAGG模块来测试同样的执行8次斐波那契数组计算,看看成绩如何?

function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}
console.time('8 thread');var numThreads=8;//创建线程池,最大数为8var threadPool=require('threads_a_gogo').createPool(numThreads).all.eval(fibo);//为线程池注册程序var i=8;var cb =function(err,data){//注册线程执行完毕的回调函数
    console.log(data);if(!--i){
        threadPool.destroy();
        console.timeEnd('8 thread');}}
threadPool.any.eval('fibo(40)', cb);//开始向线程池中执行fibo(40)这个任务

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

最重的结果:

1655801411655801411655801411655801411655801411655801411655801411655801418 thread:9510ms

相比不使用多线程模型的node,使用了TAGG模块之后,我们在4CPU服务器上的测试结果要快上一倍还不止。
到这里我们看上去找到了一个比较完美的解决方案应对CPU密集型任务,但是可能有同学会说,我可以使用cluster来做相同的事情,下面我们来做一个使用cluster计算这些任务的情况:

var cluster =require('cluster');var numCPUs =8;function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}
console.time('8 cluster');if(cluster.isMaster){// Fork workers.for(var i =0; i < numCPUs; i++){
    cluster.fork();}var i =8;
  cluster.on('exit',function(worker, code, signal){if(!--i){
            console.timeEnd('8 cluster');
            process.exit(0);}});}else{
    console.log(fibo (40));
    process.exit(0);}

代码上的复杂程度比使用TAGG要高的多,而且如果是动态计算斐波那契数组的结果,编码将更加困难,需要在fork时挂上不同的参数,出错的几率也更大。同时还有更重要的一个事情,如果是创建一个http服务器,如果4个cluster都在计算fibo,那第5个请求node将无法处理,而是用TAGG则还是能够正常处理的,所以cluster并不能解决单线程模型的cpu密集计算带来的阻塞问题,我们看下测试结果:

1655801411655801411655801411655801411655801411655801411655801411655801418 cluster:11925ms

TAGG模块还有其他更多的功能,比如事件触发,平滑退出,查看线程工作状态等等,总之TAGG模块给node注入了新的活力,让node一直饱受诟病的处理cpu密集任务问题得到了一个妥善的解决,就算你不擅长c++代码,也能够轻松编写出多线程的真正的非阻塞node程序了。


标签:node,function,console,nodejs,阻塞,40,fibo,线程,多线程
From: https://blog.51cto.com/u_2700990/6318860

相关文章

  • [nodejs]国内npm安装nodejs modules失败的几个解决方案
    使用npm安装node模块时经常有卡住安装失败的情况,如图所示。原因在于npm服务器在美国,还有就是某强大的防火墙作用。这样的问题导致很多新手放弃使用node,几乎每天都有新手再问这个问题。现在分享一下解决这个问题的解决方案1.可以通过一劳永逸的方式修改代理地址2.更换npm源拼rp (......
  • Java 网络编程 —— 实现非阻塞式的客户端
    创建阻塞的EchoClient客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求publicclassEchoClient{privateSocketChannelsocketChannel=null;publicEchoClient()throwsIOException{socketChannel......
  • NET 高级编程知识--多线程async/await
    NET高级编程知识一.多线程async/awaitasync:的价值1.降低了线程的数量;2.降低了cpu的负载;3.asnync/await提高了吞吐,只负责发命令,然后就忙别的事去了,不需要等待,事完成前不浪费资源,完成后在来线程处理,还能复用线程,不开新线程。asnyn:并发不高,线程较多,cpu开销不大,使用了DAM异......
  • nodejs的cookie实现module(cookie.js)
     文件名:cookie.js//Directlysendcookietosystem,ifit'snode.jshandler,send://request.headers.cookie//Ifit'ssocket.iocookie,send://client.request.headers.cookiemodule.exports.cookie=function(co){this.cookies={};co&......
  • nodejs 解析php的session_decode 的module(session_decode.js)
    文件名:session_decode.js//exports.session_decode=function(){//varmessage="Hellofromtheohaitheremodule";//returnmessage;//};exports.session_decode=function(data){//*example:session_decode('firstName|s:5:&quo......
  • nodejs npm国内镜像
    镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在):1.通过config命令npmconfigsetregistryhttp://registry.cnpmjs.orgnpminfounderscore(如果上面配置正确这个命令会有字符串response)2.命令行指定npm--registryhttp://registry.cn......
  • 多线程
    进程和线程的基本术语进程:一个程序执行时,系统会给它划分一些内存区域,这就是一个进程线程:一个进程包含多个线程,java程序至少至少包含三个线程,main()主线程、gc()垃圾回收线程和异常处理线程并行:并发:一个CPU执行多个任务,用于商城中的秒杀多线程可以提高程序的相应,对图形化应用......
  • 多线程
    什么是多线程?首先要了解进程,一个进程可以启用多个线程。进程:1.可以看做一个公司,线程就是公司的员工。2.进程A和进程B的内存资源独立不共享3.进程是线程的执行单元线程:1.线程A与线程B之间堆内存和方法区共享,栈是独立的,一个线程一个栈。......
  • 多路复用IO模型和非阻塞IO模型的区别?
    常见的LinuxIO模型:阻塞IO模型:当应用程序调用read或write等IO操作时,如果内核没有准备好数据,那么应用程序就会一直阻塞等待,直到内核准备好数据后才会返回。在这种模型下,应用程序通常只能同时处理一个连接,效率较低。非阻塞IO模型:当应用程序调用read或write等IO操作后,如果内核没有......
  • MS SQL Server 排查阻塞和查找被锁语句
    --方法1SELECT'资源类型'=t1.resource_type,'来源数据库'=CONVERT(CHAR(25),DB_NAME(resource_database_id)),'数据库中与资源相关联的实体的ID'=t1.resource_associated_entity_id,'锁模式'=t1.request_mode, --锁的模式:S-共享锁,U-更新锁,X-排他锁,IS/IU/IX-意向......