开启Nodejs之旅3--异步式 I/O 与事件式编程

146 阅读4分钟

  I/O概念:  input/output(一般指磁盘读写或网络通信);  异步式I/O是非阻塞input/output;  同步式I/O是阻塞式input/output。

 阻塞的定义:线程在执行中如果遇到磁盘读写或网络通信,通常要耗费较长的时间,这时操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞当I/O操作完毕时,操作系统将这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行这种I/O模式就是通常的同步式I/O(Synchronous I/O)或阻塞式I/O(Blocking I/O)。
  为了更好的理解这些专业术语,用现实生活中的列子,客户去银行窗口办理业务(cpu),当前1号柜台还在办理业务,电脑叫号不会分给1号柜台(剥夺cpu的处理权),此时电脑检查2号柜台是否有人在办业务,没人办业务,将此人分给2号柜台去办理(将资源让给2号线程),1号柜台业务办理完了,可以叫下一个客户的人来此柜台办业务(此时恢复期cpu的控制权),类似这种模式,大部分后台语言对于高并发的处理就是通过分配给不同的线程来处理多个业务(如php ,java等)

非阻塞的定义:则针对所有I/O操作不采用阻塞的策略。当线程遇到I/O操作时,不会以阻塞的方式等待I/O操作的完成或数据的回而只是将I/O请求发送给操作系统,继续执行下一条语句。当操作系统完成I/O操作时,以事件形式通知执行I/O操作的线程,线程会在特定时候处理这个事件。为了处理异步I/O,线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理。
客户去银行窗口办业务恰好赶上大中午的时间,但一般中午银行最多留一个人在窗口值班,其他窗口都暂停办理业务,此时中午来了一大批客户办理业务,A客户办理的时候忘记带关键的资料或者身份证,需要叫人送来,此时柜台人员发现需要等他就叫他先在旁边等候,资料到齐了再给他办理业务,先给他挂单在电脑系统里面(发送给操作系统),轮下一个客户办理业务(继续执行下条语句),下一个办完,就问原来A客户资料齐了没(不断检查未处理的事件),没到继续办其他人的业务,当原来的客户资料已经送来此时告诉柜台人员(以事件形式通知操作的线程),柜台人员办完当前业务,立马给他办业务,之后再给其他人办业务。这种模式是非阻塞式的模式处理(node采用就是这种非阻塞式模式处理高并发)


为了更好地理解,如下图




单线程事件驱动的异步式I/O比传统的多线程阻塞式I/O究竟好在哪里呢?
异步式I/O就是少了多线程的开销。对操作系统来说,创建一个线程的代价是十分昂贵的,
需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,CPU的缓存被
清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性






Node.js所有的异步I/O操作在完成时都会发送一个事件到事件队列。在开发者看来,事件由
EventEmitter对象提供。前面提到的fs.readFile和http.createServer的回调函数都是通过EventEmitter来实现的。下面我们用一个简单的例子说明EventEmitter的用法: