异步I/O:
在我们写前端代码的时候,经常会遇到的异步的一些操作,比如ajax
,setTimeout
,浏览器事件
等,虽然js执行是单线程的,但是浏览器却是有多个线程的,例如发起ajax
请求,浏览器会另起一个http
线程。Node类似,js执行为单线程,但是遇到一些I/O操作,提供了异步的方式。
关于同步和异步,请移步我之前写的一篇文章.
这篇文章算是对于《深入浅出Node.js》第三章内容的一个摘录和小结:
在了解Node.js中
的异步I/O前,先了解下关于阻塞和非阻塞的概念:
阻塞I/O:在Node.js执行过程中,如果遇到了磁盘的读写或网络请求时,一般可能会耗费比较多的处理时间,这个时候操作系统会剥夺Node.js这个线程的控制权,这时Node.js便停止了执行,等待系统内核层面完成所有的操作,当
I/O操作
完成后,操作系统会再将CPU的控制权返还给Node.js这个线程,然后Node.js继续向后执行代码。这个便是阻塞的I/O
;非阻塞I/O:当Node.js遇到耗时的I/O时,会将这个I/O的请求交给操作系统,操作系统会立马返回当前调用的状态,此时Node.js会继续向下执行代码,不会发生等待的情况,但是Node.js会重复调用I/O操作来确认是否完成,即轮询。
Node的异步I/O:
事件循环
首先得清楚,Node自身执行的模型----事件循环。在进程启动时,Node便会创建一个类似于while(true)
的循环,每执行一次循环体的过程我们称为Tick
。每个Tick
的过程就是查看是否有事件待处理,如果有,就去除事件及其相关的回调函数。
异步I/O
算是Node的特色,它力求在单线程上将资源分配得更加高效。异步I/O
的提出主要是为了满足单线程执行的js,当进行耗时的I/O操作
的时候,将原有等待I/O完成
的时间分配给其他需要的业务去执行。
观察者
每个事件循环中有一个或者多个观察者,而判断是否有事件要处理的过程就是向这些观察者询问是否有要处理的事件。事件循环是一个典型的生产者/消费者模型
。异步I/O、网络请求等都是事件的生产者,源源不断为Node提供不同类型的事件,这些事件被传递到对应的观察者那里,事件循环则从观察者那里取出事件并处理。
在Windows
下,这个循环基于IOCP创建
,而在*nix
下则基于多线程创建
。
请求对象
在一般的(非异步)的回调函数的调用中,函数可以由我们自行去调用:
var forEach = function(list, callback) {
for(var i = 0; i < list.length; i++) {
callback(list[i], i, list);
}
}
但是在Node的异步I/O
过程当中,回调函数并不由开发者来调用。浏览器中的异步事件同理,具体内容请戳我。在js发起调用到内核完成I/O操作
的过渡过程当中,存在一种中间产物,叫做请求对象
。
例如Node.js提供的核心fs模块,要去打开一个文件,fs.open(path, flags, mode, callback)
。js调用Node.js的核心模块,核心模块去调用C++核心模块去进行下层的操作。再调用底层代码时,创建了一个请求对象,从js层面传入的参数和callback
都被封装在这个请求对象上。其中callback
被设置在这个对象的一个属性上。
以下内容是在windows
环境下Node.js异步I/O模型
。
在windows
下,这个对象被推入线程池中等待执行。此时,js调用立即返回,由js层面发起的异步调用的第一个阶段就此结束。Node.js再次获得了CPU
的使用权,Node.js可以继续执行当前任务的后续操作。当前I/O操作
在线程池中等待执行,不管它是否阻塞I/O
,都不会影响到js线程后续执行,如此就达到了异步的目的。
请求对象
是异步I/O
过程中的重要中间产物,所有的状态都保存在这个对象中,包括送去线程池等待执行以及I/O操作完毕后的回调处理。
执行回调
线程池中的I/O操作
调用完毕之后,在每次tick
执行过程中,会调用IOCP
提供的相关方法检查线程池中是否有执行完的请求,如果会将请求对象加入到I/O观察者队列
中,然后将其当做事件处理。
Windows
下主要通过IOCP
来向系统内核发送I/O调用
和从内核获取已完成的I/O操作
,配以事件循环,以此完成异步I/O
的过程。在Linux
下通过epoll
实现这个过程,FreeBSD
下通过kqueue
实现,Solaris
下通过Event ports
实现。不同的是线程池在windows下由内核(IOCP)直接提供,*nix系列下有libuv执行实现。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。