zoukankan      html  css  js  c++  java
  • node.js 异步式I/O 与事件驱动

    Node.js 最大的特点就是异步式 I/O(或者非阻塞 I/O)与事件紧密结合的编程模式。这
    种模式与传统的同步式 I/O 线性的编程思路有很大的不同,因为控制流很大程度上要靠事件
    和回调函数来组织,一个逻辑要拆分为若干个单元。

    阻塞与线程
    什么是阻塞(block)呢?线程在执行中如果遇到磁盘读写或网络通信(统称为 I/O 操作),
    通常要耗费较长的时间,这时操作系统会剥夺这个线程的 CPU 控制权,使其暂停执行,同
    时将资源让给其他的工作线程,这种线程调度方式称为 阻塞。当 I/O 操作完毕时,操作系统
    将这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行。这种 I/O 模式就是通
    常的同步式 I/O(Synchronous I/O)或阻塞式 I/O (Blocking I/O)。
    相应地,异步式 I/O (Asynchronous I/O)或非阻塞式 I/O (Non-blocking I/O)则针对
    所有 I/O 操作不采用阻塞的策略。当线程遇到 I/O 操作时,不会以阻塞的方式等待 I/O 操作
    的完成或数据的返回,而只是将 I/O 请求发送给操作系统,继续执行下一条语句。当操作
    系统完成 I/O 操作时,以事件的形式通知执行 I/O 操作的线程,线程会在特定时候处理这个
    事件。为了处理异步 I/O,线程必须有事件循环,不断地检查有没有未处理的事件,依次予
    以处理。
    阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞
    模式下,一个线程永远在执行计算操作,这个线程所使用的 CPU 核心利用率永远是 100%,
    I/O 以事件的方式通知。在阻塞模式下,多线程往往能提高系统吞吐量,因为一个线程阻塞
    时还有其他线程在工作,多线程可以让 CPU 资源不被阻塞中的线程浪费。而在非阻塞模式
    下,线程不会被 I/O 阻塞,永远在利用 CPU。多线程带来的好处仅仅是在多核 CPU 的情况
    下利用更多的核,而Node.js的单线程也能带来同样的好处。这就是为什么 Node.js 使用了单
    线程、非阻塞的事件编程模式

    异步式 I/O 就是少了多线程的开销。对操作系统来说,创建一个线程的代价是十分昂贵的,
    需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,CPU 的缓存被
    清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。①
    当然,异步式编程的缺点在于不符合人们一般的程序设计思维,容易让控制流变得晦涩
    难懂,给编码和调试都带来不小的困难。习惯传统编程模式的开发者在刚刚接触到大规模的异
    步式应用时往往会无所适从,但慢慢习惯以后会好很多。尽管如此,异步式编程还是较为困难,
    不过可喜的是现在已经有了不少专门解决异步式编程问题的库(如async),参见6.2.2节。

    让我们看看在 Node.js 中如何用异步的方式读取一个文件,下面是一个例子:
    //readfile.js
    var fs = require('fs');
    fs.readFile('file.txt', 'utf-8', function(err, data) {
    if (err) {
    console.error(err);
    } else {
    console.log(data);
    }
    });
    console.log('end.');
    运行的结果如下:
    end.
    Contents of the file.

    Node.js 也提供了同步读取文件的 API:
    //readfilesync.js
    var fs = require('fs');
    var data = fs.readFileSync('file.txt', 'utf-8');
    console.log(data);
    console.log('end.');
    运行的结果与前面不同,如下所示:
    $ node readfilesync.js
    Contents of the file.
    end.

    fs.readFile 调用时所做的工作只是将异步式 I/O 请求发送给了操作系统,然后立即
    返回并执行后面的语句,执行完以后进入事件循环监听事件。当 fs 接收到 I/O 请求完成的
    事件时,事件循环会主动调用回调函数以完成后续工作。因此我们会先看到 end.,再看到
    file.txt 文件的内容

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

    var EventEmitter=require("events").EventEmitter;
    var event=new EventEmitter();

    event.on("some_event",function(){
    console.log("some event occured");
    });
    setTimeout(function(){
    event.emit("some_event");
    },1000);

    运行这段代码,1秒后控制台输出了 some_event occured.。其原理是 event 对象
    注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在1000毫秒以后向
    event 对象发送事件 some_event,此时会调用 some_event 的监听器。
    我们将在 4.3.1节中详细讨论 EventEmitter 对象的用法。
    Node.js 的事件循环机制
    Node.js 在什么时候会进入事件循环呢?答案是 Node.js 程序由事件循环开始,到事件循
    环结束,所有的逻辑都是事件的回调函数,所以 Node.js 始终在事件循环中,程序入口就是
    事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出 I/O 请求或
    直接发射(emit)事件,执行完毕后再返回事件循环,事件循环会检查事件队列中有没有未
    处理的事件,直到程序结束。图3-5说明了事件循环的原理。

    与其他语言不同的是,Node.js 没有显式的事件循环,类似 Ruby 的 EventMachine::run()
    的函数在 Node.js 中是不存在的。Node.js 的事件循环对开发者不可见,由 libev 库实现。libev
    支持多种类型的事件,如 ev_io、ev_timer、ev_signal、ev_idle 等,在 Node.js 中均被
    EventEmitter 封装。libev 事件循环的每一次迭代,在 Node.js 中就是一次 Tick,libev 不
    断检查是否有活动的、可供检测的事件监听器,直到检测不到时才退出事件循环,进程结束。

  • 相关阅读:
    jython resources
    Installing a Library of Jython ScriptsPart of the WebSphere Application Server v7.x Administration Series Series
    jython好资料
    ulipad install on 64bit win7 has issue
    an oracle article in high level to descibe how to archtichre operator JAVA relevet project
    table的宽度,单元格内换行问题
    Linux常用命令大全
    dedecms系统后台登陆提示用户名密码不存在
    登录织梦后台提示用户名不存在的解决方法介绍
    Shell常用命令整理
  • 原文地址:https://www.cnblogs.com/youxin/p/3956937.html
Copyright © 2011-2022 走看看