node.js 的异步模型

node.js 的异步模型 

一、基本概念 
  阻塞式IO:cpu一直等到数据准备好了之后才会工作,即从函数调用开始,一直到数据准备好这段时间cpu是干等着的 
  非阻塞式的IO:数据准备好了之后发一个信号,此后cpu才会处理,即cpu不会干等着 

  阻塞式:相当于是轮训、非阻塞式相当于是中断 

  同步执行:语句的先后顺序就是cpu执行指令的顺序 
  异步执行:写在前面的语句不一定先执行,具体执行的时间不确定(一般是事件触发的) 

  可以看出,非阻塞io肯定要与异步执行结合才能保证程序逻辑的正确性,node.js中使用的是非阻塞的io 

二、初衷 
  node.js的初衷是解决IO密集型的应用(与此相对的是cpu密集型)。在实际应用中,为了实现应用层的并发性,大多数程序都会使用多线程或者是多进程(尤其是server这种需要同时并发对外提供服务的程序)。程序逻辑一般都会处理数据,当出现数据冲突时多线程必须串行执行(即加锁),这样的话多开的线程实际上是没有达到并发的效果的,同时因为多线程时CPU要频繁的调度,反而会增加系统负担(对于实时性的server 如网游服务器,IM服务器等一般都不会开很多的线程,这些server最大的瓶颈都是网络或数据库等IO操作)。经过分析可知,系统的瓶颈主要是在IO上,即cpu执行的快没有用,整个程序的性能是综合的结果,从吞吐率上来说,多线程并没有比单线程快多少。 

三、node.js的哲学 
  既然瓶颈是在IO,那我就对付这个IO。所以node.js采用单线程执行模型:所有需要cpu等待的东西,全部移出执行队列,等到IO执行好了之后,再放入一个回调进来。这样做的话,cpu就会一直做‘有意义’的事情。 
这样做的好处:1、不用多线程,所以不用加锁、不用调度, cpu的吞吐率高很多。2、不用处理线程同步带来的n多的诡异问题。 
坏处:要求程序员要有异步的思维模式,即下一行代码不一定是在什么时候执行。如果逻辑间有前后依赖关系,一定要将后面的逻辑放入回调中。 

三、单线程模型下如何实现用户级的并发 -----------基于事件的timer 
  执行模型是单线程的,但是应用的逻辑可能要求是并发的,那么该如果实现用户级的并发呢?如果用c语言来实现,一般采用 经典的消息队列+ worker thread + 回调函数, 
在node.js中,一般是使用timer,如果是无限循环的逻辑 一般包装一个scheduler就OK了。 

四、node.js实现的异步事件的相关源代码(c实现) 
  node.js利用uvlib来实现跨平台,windows下事件循环的实现 在uvlib的 src/win/core.c --------UV_LOOP_ONCE 

此宏的主要代码如下: 
uv_update_time((loop));                                                   \ 
    uv_process_timers((loop));                                                \ 
                                                                              \ 
    /* Call idle callbacks if nothing to do. */                               \ 
    if ((loop)->pending_reqs_tail == NULL &&                                  \ 
        (loop)->endgame_handles == NULL) {                                    \ 
      uv_idle_invoke((loop));                                                 \ 
    }                                                                         \ 
                                                                              \ 
    uv_process_reqs((loop));                                                  \ 先处理 
    uv_process_endgames((loop));                                              \ 
                                                                              \ 
    if ((loop)->refs <= 0) {                                                  \ 
      break;                                                                  \ 
    }                                                                         \ 
                                                                              \ 
    uv_prepare_invoke((loop));                                                \ 
                                                                              \ 
    poll((loop), (loop)->idle_handles == NULL &&                              \ 后收集事件 
                 (loop)->pending_reqs_tail == NULL &&                         \ 
                 (loop)->endgame_handles == NULL &&                           \ 
                 (loop)->refs > 0);                                           \ 
                                                                              \ 
    uv_check_invoke((loop)); 



概括一下:执行引擎是一个无限循环的过程,每一次的循环都可以看做一个tick()。 一个tick内基本逻辑是 
1、更新全局定时器, 
2、检查timer是否到期(到的话就放入执行队列,在下一个tick中来执行), 
3、执行此tick内的所有回调 -------schedule的过程 
4、检查是否要退出 
5、从OS处取出网络socket,文件操作等IO完成事件, 

PS:因为每个tick要处理的事件数量是随机的,所以2个tick间的deltaTime不是固定不变的,所以node.js不保证帧率而是保证吞吐率,所以每一次tick的顺序是schedule(), poll()



原文 :http://eric-weitm.iteye.com/blog/1481677

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值