zoukankan      html  css  js  c++  java
  • nodejs是单线程

    你不妨先思考一个问题:
    在单核时代,PHP之类多线程或者多进程的,是怎么处理并发的?是排队吗?

    答案是:的确就是排队。但是并不是一定要处理完请求1才能去处理请求2:实际上请求的处理过程中,有很多的时间是耗在IO等其他地方,这时可以切换去处理其他请求,把等待的时间可以充分利用起来,达到更高的吞吐量。切换调度的策略是线程库,或者OS实现的,由于每个进程/线程需要占用不少资源(典型的是内存,一个线程通常需要2M的栈空间),更重要的是,线程/进程切换时的开销是非常大的。

    既然如此,为何不让线程自己来管理呢?于是大家都开始用select/poll了,由于减少了上面说到的开销,吞吐量显著提高。这就是所谓的IO多路复用。但是大家用着用着,发现并发到了一定量级又上不去了怎么办?这就是所谓的c10k problem了。

    经查,发现是select用O(n)的效率不断地去查看那些fd,效率太低。于是Linux供出了epoll,bsd供出了kqueue,windows供出了IOCP,通过在内核中提供callback机制的方式,epoll在内部使用RBTree把O(n)降到了O(logn)(感谢鱼丸粗面纠正)。于是并发量就上去了。

    大家熟知的libevent/libev基本上就是把不同系统的类似机制封装好,为上层提供一个统一的接口,方便开发和移植。这个还有个装逼的说法叫做reactor模式。

    最后回到你的问题,nodejs的确就是排队的。关键在于怎么在排队的时候充分利用插队策略来达到最高的效率。nodejs内部的实现我没有具体了解,不过应当是使用类似协程这样的技术,在需要阻塞的地方,从底层入手引入调度机制,从而使得上层看起来似乎仍然是同步、阻塞的(感谢@TonySeek的指正,nodejs用的是callback套callback的方式,详见评论;我说的那个是python+gevent的实现方式) 。

    扩展一下,对于如何充分利用多核来提高效率的问题,答案就是:多开几个进程(补充:这里特指针对单进程而言;而且并不是进程越多越好,一般而言与CPU线程数相当为佳)。

    1

    node 用的是作者自己开发的 libuv,和 libev/libevent 很相似,只不过在 Windows 下不是用 select 而是用 IOCP。协程只是一种让 callback 扁平化的方式,node 缺的就是这个,callback 套 callback 非常恶心。

    #1 TonySeek · 2013年04月23日 · 回复

    1

    提高效率也不应该简单的总结为多开几个进程,同一任务的线程(进程)数大于处理器支持的物理最大线程数时,不仅不能得到更高的效率,反而会增加调度开销。尤其是 Web 服务器这类 IO 密集型的,一般进程数等于处理器核数就足够。

    #2 TonySeek · 2013年04月23日 · 回复

    展开评论
    4
    奈文 804 2013年04月17日 回答 · 2013年04月23日 更新

    其实现在的异步模型大同小异,大致过程如下(分三层):

    1.(最重要的)维护一个事件反应堆,用epoll或者select或者kqueue来做,反应堆的作用就是用同步的方式处理异步问题,在反应堆上注册好事件后如果相应的事件发生,就调用其回调函数,一般情况下反应堆是一个进程内全局唯一的。

    2.上层的buffer,维护一系列的buffer用于管理每一个连接的数据,可以把buffer看做是一个对象。一般在一个连接到达的时候分配一个buffer对象,然后上层的连接注册事件的时候是注册到buffer上,buffer再注册到反应堆中。

    3.就是一个个的连接对象,把每一个来自外部的连接都抽象为一个具体的对象,用于管理每一个连接,其中这个对象就包含了上面所说的buffer对象和其他一些状态。

    处理并发的过程就是这样的:

    1.为监听套接口在反应堆注册一个事件,此事件发生调用对应的回调,一般情况是accept这个连接,然后为这个连接创建连接对象,统一管理。

    2.为此连接创建buffer对象,并注册对应的读写错误事件的回调(上层对于buffer的读写事件回调都是业务层来控制的了).

    3.(所谓的排队机制也不是完全正确)在加入监听队列后是离散的,准确来说epoll中是由一颗红黑树维护的,每一个事件的先后顺序跟它达到的顺序有关。

    4.维护了众多的连接对象,也就是这里的并发情况了,如果有事件发生会调用回调来处理,理论上无阻塞情况减少了很多CPU的wait,这部分时间用于处理真正的业务,所以异步模型能够带来很高的CPU处理能力,减少等待,单位时间处理的事件越多,从外部来看并发就很高,实际上也是一个串行的工作状态,但是串行过程没有等待。

    1
    维生素CC 77 2013年04月17日 回答

    对于node可以从process.nextTick()入手看

    1
    pubby_sk 248 2013年04月23日 回答

    既然是单线程异步,那么只能对IO密集型的业务能显著提高并发,对CPU密集型业务没有太大优势。

    1
    blankyao 294 2013年04月23日 回答

    在node.js里面只有你写的代码是单线程的,其内部并不是单线程的,只是它实现了这个机制,让用户写的代码都是单线程的。推荐篇文章给你: Node is Not Single Threaded

    0
    dyzdyz010 1k 2013年04月17日 回答

    参考Node.js开发指南

    0
    GR_Art 5 2014年02月20日 回答

    可以看下这往篇文章

  • 相关阅读:
    bzoj4950
    P1377发奖金
    环信SDK与Apple Watch的结合(1)
    环信SDK与Apple Watch的结合(2)
    【Objective-C】0-第一个OC的类
    【Objective-C】4-空指针和野指针
    【Objective-C】3 -self关键字
    【Objective-C】2.自定义构造方法和description方法
    Swift函数|闭包
    Swift枚举|结构体|类|属性|方法|下标脚本|继承
  • 原文地址:https://www.cnblogs.com/viewcozy/p/4859024.html
Copyright © 2011-2022 走看看