对于Node中的异步I/O调用,从发出调用到回调执行,看起来像普通的js异步,但是流程却和普通js那些消息队列完全不同,整个过程经历了哪些?

下面以Windows平台下为例:

一,异步调用第一阶段:

1,首先JavaScript调用Node的核心模块,核心模块再调用C++内建模块,内建模块通过libuv进行系统调用。(这里的libuv是抽象封装层,使得平台兼容性的判断都由这一层来实现,并保证上层的Node与下层的自定义线程及IOCP之间互相独立。Node在编译期间会判断平台条件,选择性编译unix目录或是win目录下的源文件到目标程序中。)

内建模块调用过程中,会创建一个FSReqWrap请求对象,从JavaScript层传入的参数和当前方法都被封装在这个请求对象中,而回调函数则被设置在这个请求对象的oncomplete_sym属性上。

2,对象包装完毕后,在Windows下,则调用QueueUserWorkItem()方法将这个FSReqWrap请求对象推入线程池中等待执行,该方法使用代码如下:

QueueUserWorkItem( &uv_fs_thread_proc, req, WT_EXECUTEDEFAULT )

这个方法接收三个参数,第一个是将要执行的方法的引用,这里的例子是uv_fs_thread_proc,这里实际上就是要执行的I/O操作实际对应的方法,第二个是执行这个方法运行时所需的参数,第三个是执行的标志。

3,当线程池中有可用线程时,就会调用这个引用方法,这里就是uv_fs_thread_proc()方法,这个方法会根据传入参数的类型调用相应的底层函数。

至此,JavaScript调用立即返回,可以继续执行当前任务的后续任务,当前的I/O操作在线程池中等待执行,不会影响到JavaScript线程的后续执行,如此达到异步的目的。

第一部分关键就是这个请求对象,它是异步I/O过程中重要的中间产物,所有的状态都保存在这个对象中,包括送入线程池等待执行以及I/O操作完毕后的回调。

二,第二阶段

1,线程池中的I/O操作调用完毕之后,会将获取的结果存储在req -> result属性上,然后调用PostQueueCompletionStatus()通知IOCP,告知当前对象操作完成。这个PostQueueCompletionStatus方法的作用是向IOCP提交执行状态,并将线程归还线程池。

2,而提交的这个执行状态,可以通过GetQueueCompletionStatus()获取,很好理解,一个post,一个get,而谁来调用这个get方法呢?就是事件循环中的I/O观察者。每次事件循环,它会调用IOCP相关的GetQueueCompletionStatus方法检查线程池中是否有执行完的请求,如果存在,会将请求对象加入到I/O观察者的队列中,然后将其当做事件处理。

3,I/O观察者回调函数的行为就是取出请求对象的result属性作为参数,取出oncomplete_sym属性作为方法,然后调用执行,以此达到调用回调函数的目的。

以上就是完整的从开始调用异步I/O,到执行完毕后调用回调函数的整个过程。

总结:

上面的一些对象名、函数名、属性名比较多,且长,这里大概讲一下,不需要死记这些名词。

异步调用发起,把异步I/O、参数、状态等,以及最重要的回调函数封装进一个请求对象,然后将这个请求对象推入线程池等待执行,如果线程池中有可用的线程,就执行请求对象中的I/O操作实际对应的方法,参数也从里面拿,执行完成后,将结果放在请求对象中,然后调用一个postbalabala方法,通知IOCP调用完成了并且归还线程。而事件循环的过程中,I/O观察者会不断地轮询是否有可用的请求对象,具体方式就是调用一个getbalabala方法来查看执行状态,如果有了就表示有I/O任务执行完成了,那么就调用请求对象里的回调函数和执行结果,然后继续轮询。

所谓的事件驱动,其实本质上面已经介绍过了,即通过主循环加事件触发的方式来运行程序。简单来说,当进来一个新的请求的时,请求将会被压入队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数。这个请求就是上面提到的请求对象。

====================

2019.03.07

同步和异步看主线程,阻塞和非阻塞看I/O线程

以上两张图截选自朴灵老师的深入浅出nodejs,图一是整个异步IO的流程,图二也是异步IO,只是少一个事件循环的流程图;

我们刚刚说同步异步看主线程,就是图二里的异步调用那个模块和图一里的主线程;但是我们要注意,node始终是单线程,也就是说在每一个线程上都是按顺序执行。当我们有异步调用发生时,比如有100个异步调用,也是完全按顺序执行,只不过每次调用,会很快的结束(三个步骤:封装请求对象,设置参数和回调,推入线程池),然后开始下一次调用(继续三步骤),如果是同步的,那么下一次调用一定要等上一次调用有结果返回了,才会进行。就像我们之前一篇随笔说的,异步和同步的区别在于消息通知是主动还是被动,是主动去关心还是根本不关心。

那我们再来看阻塞和非阻塞,当线程池有可用的线程时,(待续)

end

Nodejs之路:异步I/O的过程的更多相关文章

  1. NodeJS中的异步I/O、事件驱动

    nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...

  2. AJAX异步请求原理和过程

    AJAX 指异步 JavaScript 及 XML(Asynchronous JavaScript And XML),它不是一种新的编程语言,而是一种使用现有标准的新方法. AJAX 基于 JavaS ...

  3. nodejs之路-[0]安装及简易配置

    题外话: 之前写过ubuntu下编译nodejs- 传送门:Ubuntu15.04编译安装nodejsV0.12.3 只是如今基本在win下做开发了-. 就以这篇帖子为开头,作为我踏上nodejs之路 ...

  4. nodejs 中的异步之殇

    nodejs 中的异步之殇 终于再次回到 nodejs 异步中,以前我以为异步在我写的文章中,已经写完了,现在才发现,还是有很多的地方没有想清楚,下面来一一说明. 模块同步与连接异步 大家应该,经常使 ...

  5. Nodejs之路:非I/O的异步API

    本篇主要介绍setTimeout,setInterval,setImmediate和process.nextTick. 1,定时器 Node中的定时器和浏览器中用法一致.区别在于:在Node中,执行到 ...

  6. NodeJS学习之异步编程

    NodeJS -- 异步编程 NodeJS最大的卖点--事件机制和异步IO,对开发者并不透明 代码设计模式 异步编程有很多特有的代码设计模式,为了实现同样的功能,使用同步方式和异步方式编写代码会有很大 ...

  7. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

  8. Nodejs爬虫进阶=>异步并发控制

    之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一 ...

  9. 如何优雅的处理Nodejs中的异步回调

    前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O ...

随机推荐

  1. GraphicsMagick命令

    [ convert | identify | mogrify | composite | montage | compare | display | animate | import | conjur ...

  2. mysql 5.7 修改字符编码

    在my.ini文件中添加 [mysqld]character-set-server = utf8 [client]default-character-set = utf8

  3. Servlet-知识点

    2018年10月05日 16:52:56 yigg 阅读数:38   1.JavaWeb开发的目录结构 https://blog.csdn.net/u012661010/article/details ...

  4. FTP管理常用命令

    #新增用户liuhui,指定群组为groupa,附加群组为groupb,家目录为/ftp/groupbuseradd -g groupa -G groupb -d /ftp/groupb linhui ...

  5. http-cookie、session、Token

    无状态  cookie技术的发展 客户端发送请求登录系统 Set-Cookie管理Cookie信息并且返回给客户端 拿着返回的cookie请求Server cookie 管理session 发送登录信 ...

  6. nginx 301跳转

    server { server_name xxxx.com; return 301 $scheme://www.xxxx.com$request_uri; }

  7. 一个free异常引发的异常

    有同事反馈说自己的线程不工作,查看堆栈发现其打印如下: # # # # # # # # , info= # <signal handler called> # # # # # # # , ...

  8. 在delphi中XLSReadWriteII.组件的应用实例(2)

    第三方组件:XLSReadWriteII.v.5.20.67_XE3 实例源码如下:   unit Unit1; interface uses Winapi.Windows, Winapi.Messa ...

  9. TypeError: Fetch argument None has invalid type <type 'NoneType'>

    (fetch, type(fetch)))TypeError: Fetch argument None has invalid type <type 'NoneType'> 我的解决方案是 ...

  10. C#发送QQ邮件

    1.首先配置一下发件人的账号密码(密码根据自己所选择的的邮箱填写,此处不做展示) <?xml version="1.0" encoding="utf-8" ...