异步I/O

来源:互联网 发布:人工智能论文话题英文 编辑:程序博客网 时间:2024/05/29 12:18
          PHP语言的设计最能体现不使用异步、屏蔽异步;PHP对调用底层不仅屏蔽了异步,甚至连多线程都不提供。PHP语言从头到脚都是以同步阻塞的方式来执行的。PHP的优点十分明显,利用程序员书讯编写的业务逻辑;它的缺点在于小规模站点中基本不存在,但是在复杂的网路应用中,阻塞导致它无法更好地并发。所以业界有句话:PHP的王道是面向过程。
          Node是将异步作为主要编程方式和设计理念的,异步I/O、事件驱动和单线程是Node的基调。

1、Node为什么要I/O
这是因为现代的网络要求有关,现在已经不是一台二台机器能够使用的的了,在跨网络的结构下,并发已经是现代编程中的标准配备的。
异步的概念之所以火起来,是因为在浏览器中JavaScript在单线程上执行,而且它还与UI渲染公用一个线程。所以采用异步请求,在网络下载执行期间,JavaScript和UI的执行都不会处于等待状态。
在我们提到Node的时候,异步、非阻塞、回调、事件字眼都会出现,其中异步和非阻塞听起来好像是同一回事,单实际上,异步同步、阻塞非阻塞是两码事。操作系统对I/O只有两种方式:阻塞与非阻塞。在调用阻塞时,应用程序必须等待I/O完成才返回结果。
阻塞I/O造成CPU等待I/O,浪费时间,CPU的处理能力不能得到充分利用,非阻塞针对阻塞而言,应用程序在调用和马上就返回继续执行而不用等待。

2、事件循环
Node 自身的执行模式--事件循环
在启动进程时,Node便会创建一个类似PHP while(true)的循环,每次都会检查是否有事件处理,如果有,就取出相关的回调函数,如果存在回调函数就执行它们,然后进入下一个循环。
每个事件都有一个或多个观察者,而判断是否有事件需要处理就是这些观察者询问是否有处理的事件。就如同餐厅的服务员,收到一张或多张菜单,告诉厨房做菜;而餐厅有很多服务员;这里的服务员就是观察者。如果在循环中没有发现有观察者,那么Node就会结束循环。

3、请求对象
对于一般的非异步的回调函数,我们自行调用和编写
var forEvent = function (list, func) {
     for (var i = 0; i < list.lenght; i++) { func(list[i], i, list);
}
而对于Node而言,回调函数不时开发者来调用的。从我们发出回调到回调执行,中间有很多事情发生,在这过程中有一个很重要的产物:请求对象。
下面是一个例子:
fs.open = funciton(path, flags, mode, callback) {
     binding.open(pathModule._makeLong(path),stringToFlags(flags),mode, callback); ..
}
fs.open的作用是指定路径和参数去打开一个文件,获取文件的描述,这是后续所有I/O操作的初始化操作。
从JavaScript调用Node的核心模块,核心模块调用C++内建模块,内建模块通过libuv进行系统调用(底层),这就是Node的调用过程。底层会创建一个请求对象,从JavaScript中传入的参数和当前的方法都会被封装在这个请求中。在这之后,系统会将这个请求对象推入到线程池中,等待执行。
到此为止,JavaScript调用立即返回,由JavaScript层面发起的异步调用的第一阶段就完成了。JavaScript线程可以继续执行当前任务的后续工作。当前的I/O操作就在线程池中等待执行,不官它是否阻塞,都不会影响到JavaScript线程的后续执行,这就达到了异步的操作。
请求对象是异步I/O过程中的产物,所有的状态都保存在这个对象中。
4、执行回调
组装好请求对象,送入I/O线程池等待执行,它实际上完成了异步I/O的第一部分,回调通知是第二部分。线程池中的I/O操作完毕之后,会将获取的结果存储并调用另一个方法告诉我们当前对象操作已经完成。就好比餐厅的服务员知道菜已经做好了,并把等待中的菜单交给(归还)给厨房。到此,整个异步I/O的流程就完全结束。

总结:
首先我们文字描述下流程
1、异步调用:发起异步调用-封装请求对象-设置参数和回调函数-将请求对象放入线程池对象中等待执行-结束
2、线程池:满足线程可用-执行请求对象中的I/O操作-将执行完成的结果放在请求对象中-通知调用完成(菜做好了交给服务员)-归还线程-结束
3、事件循环:创建主循环-从I/O观察者取到可用的请求对象(菜单)-取出回调函数和结果调用执行-获取完成的 I/O交给观察者(线程池中通知调用完成)-结束

事件循环、观察者、请求对象、I/O线程共同构成了Node异步I/O模型的基本要素。

在Node中还存在非I/O异步的API:
1、定时器setTimeout和setInterval()
它们的区别在与setTimeout是单次,而setInterval()是多次,下面是它们的例子:
setTimeout(“alert(‘3秒钟后执行’)”, 3000);
var int=self.setInterval("clock()",50); 循环
clearInterval(int) 停止
2、process.nextTick()
process.nextTick()的意思就是定义出一个动作,并且让这个动作在下一个事件轮询的时间点上执行。
functionfoo() {    console.error('foo');}
process.nextTick(foo);console.error('bar');上面的会先输入bar,再输入foo;

3、setImmediate()
setImmediate()和process.nextTick的方法类似,都是将回调函数延迟执行。
例如:
setImmediate(function() { alert(‘bb’); };
cosole.log(‘aa);
输出aa bb
如果这样呢
process.nexTick(function(){alert(‘cc’);};
setImmediate(function() { alert(‘bb’); };
cosole.log(‘aa); 
输出:aa cc bb
所以process.nextTick优先级高于setImmediate

0 0