阻塞与非阻塞IO

单进程

在基础架构上,Node.js采用一个长期运行的进程,与Apache不同,Apache会产生多个线程(每个请求一个线程),因此需要特别注意对共享状态的修改。通过下例可以说明:

var books = ["Metamorphosis", "Crime and punishment"];

function serveBooks() {
    // 给客户端返回HTML代码
    var html = '' + books.join('
') + ''; // 恶魔出现了,把状态修改了! books = []; return html; } 因为Node.js采用单进程,因此第一次请求会返回正常结果,但是第二次请求时,由于第一次请求修改了共享状态,因此返回错误结果。

同步/异步、阻塞/非阻塞

同步/异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由调用者主动等待这个调用结果。
异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用

你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程

还是上面的例子,你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了,
当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

异步IO

阻塞与非阻塞IO_第1张图片
同步I/O.png

像Java、Python具有多线程的语言。多线程同步模式是这样的,将cpu分成几个线程,每个线程同步运行。

阻塞与非阻塞IO_第2张图片
异步I/O

Node.js单线程异步非阻塞模式,每一个计算都独占cpu,遇到I/O请求不阻塞后面的计算,当I/O完成后,以事件的方式通知,继续执行计算2。

事件驱动

阻塞与非阻塞IO_第3张图片
事件驱动
阻塞与非阻塞IO_第4张图片
事件循环

事件驱动主要思想是通过事件或状态的变化来进行应用程序的流程控制,一般通过事件监听完成,一旦事件被检测到,则调用相应的回调函数。事件驱动主要执行过程是当进来的一个新的请求的时候,请求将会被压入队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数。

线程驱动是当收到一个请求的时候,将会为该请求开一个新的线程来处理请求。而线程主要是由线程池来管理的。当线程池中有空闲的线程,会从线程池中拿取线程来处理,如果线程池中没有空闲的线程,新来的请求将会进入队列排队,直到线程池中空闲线程

调用堆栈

函数被调用时,就会被加入到调用栈顶部,执行结束之后,就会从调用栈顶部移除该函数。

function c() {
    console.log('c');
    console.trace();
}

function b() {
    console.log('b');
    c();
}

function a() {
    console.log('a');
    b();
}

a();


a -> b -> c

错误处理

Node.js应用依托于一个拥有大量共享状态的大进程中,因此一旦发生错误,在错误未被捕获的情况下,整个进程就会崩溃。
绝大部分Node异步API接收的回调函数,第一个参数都是错误对象或者是null。

你可能感兴趣的:(阻塞与非阻塞IO)