【管子先生的Node之旅·9】阻塞与非阻塞IO

2017-10-28  本文已影响0人  管子先生

阻塞

运行下列 dome 时,必须要一条一条的顺次打印出来。
换句话说,我在执行方法时必须要等待上一条语句执行完成后才能执行,因此阻塞了线程。

    // app.js
    console.log('hello World!');

    console.log('Jiaiyan');

    console.log('hello Jiaiyan!');

非阻塞

Node 使用了事件轮询,因此在这 setTimeout 时非阻塞的。
换句话来说,dome 运行到 setTimeout 时,后面的 console.log() 语句被立即执行,无需等待上一条语句是否执行完成。

    // app.js
    console.log('hello World!');

    setTimeout(() => {
        console.log('Jiaiyan');
    }, 1000);

    console.log('hello Jiaiyan!');
事件轮询?

从本质上来解释,就是 Node 会先注册事件,然后不停的询问内核这些事件是否已经分发。当事件分发时,对应的回调函数就会被触发,然后继续执行下去。如果没有事件出发,则继续执行其他代码,直到有新事件时,再去执行对应的回调函数(setTimeout 仅仅是注册了一个事件,然后让程序继续执行,所以,这是异步的)。

Node.js 单线程的世界

众所周知,Node.js不借助任何模块是以单线程的模式运行着的。
通过下述带么可以很好的验证这一点,以及展示它和事件轮询之间的关系:

    // app.js
    var start = Date.now();

    setTimeout(() => {
        console.log(Date.now() - start);
    }, 1000);

    setTimeout(() => {
       console.log(Date.now() - start);
    }, 2000);

下面是我电脑打印出来的(单位是毫秒):

image.png
输出结果,明显与之前设置的不符,产生这个问题的原因,是因为事件轮询被 JavaScript 代码阻塞了。当第一个事件分发时,会执行 JavaScript 回调函数。由于回调函数执行需要一定的时间,所以下一轮的事件轮询事件就超过了设定时间。因此, JavaScript 中的 setTimeout 并不能严格的遵守时钟设置。

如果按照事件轮询所述,正在执行任务只有一个线程,也就是说,当一个函数执行时,同一时间 不可能有第二个函数在执行,并且每一个函数都有事件延迟,相当于同一时间段内参数执行量变少,这样不更影响效率吗?Node 是如何做到高并发处理的呢?

堆栈

先看一段代码:

    http.createServer(function() {
        a();
    });

    function a() {
        b();
    }

    function b() {}

V8 在执行第一个函数时,会创建一个公共调用堆栈。如果该函数调用另一个函数,V8 就会把它添加到堆栈上去。上述代码,一旦 Http请求到达服务,Node 会先分发一个通知。然后回调函数会被执行,并且调用堆栈会变为 a => b

由于 Node 是运行在单线程的环境中,那么,当调用堆栈展开时,Node 就无法处理其他的请求了,那么并发量不久是为 1 了吗?是的,Node 并不是提供正真的并行操作,因为那样就需要引入更多的并行线程(后期会与大家一起学习 Node 多线程)。其实,在调用堆栈非常快的情况下你无需处理多个请求。这也就是为什说V8 和 非阻塞IO 是最好的搭配:V8 的运行速度非常快,非阻塞IO 确保了单线程在运行时,不会受到数据库访问与硬盘操作而导致挂起的事情了。

上一篇下一篇

猜你喜欢

热点阅读