对于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. nginx配置分发Tomcat服务,负载均衡

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中瓦片资源越来越多,如果提高瓦片的访问效率是一个需要解决的 ...

  2. 第24课 可变参数模板(5)_DllHelper和lambda链式调用

    1. dll帮助类 (1)dll的动态链接 ①传统的调用方式:先调用LoadLibrary来加载dll,再定义函数指针类型,接着调用GetProcAddress获取函数地址.然后通过函数指针调用函数, ...

  3. Navicat工具怎么连接oracle数据库

    当我们安装完oracle数据库之后,我们会想着用可视化工具连接,navicat是我们常用的可视化连接数据库的工具 当我们安装完oracle数据库的时候,我们需要先启动他 先把相应的服务启动 保证这两个 ...

  4. openStack 重新resize时会进行重新调度,可能在本机Resize 扩展资源,也可能存在的情况时 ,新扩展的资源在当前节点不足分配,整个虚拟机将进行迁移调度,进行异机迁移时需要迁移 的两台主机间能使用nova系统用户经passless登录

    openStack 重新resize时会进行重新调度,可能在本机Resize 扩展资源,也可能存在的情况时 ,新扩展的资源在当前节点不足分配,整个虚拟机将进行迁移调度,进行异机迁移时需要迁移 的两台主 ...

  5. C# 设置Excel数字格式

    数字格式使指能够控制Excel单元格中数字如何显示的格式字符串.例如,我们可以对数字12345应用数字格式“0.00”,使之显示为“12345.00”.在例如对数字12345应用“¥0.00”格式,使 ...

  6. leetcode234

    /** * Definition for singly-linked list. * public class ListNode { * public int val; * public ListNo ...

  7. 通过adb启动app应用

    由于某些原因,我需要自动启动雷电模拟器里面的一个应用.(利用Windows任务计划) 怎么自启动雷电模拟器就不用说了,很简单. 自启动app我倒是不熟悉,我没用安卓方面的知识.再官网论坛上面查到了相关 ...

  8. spring的ioc与aop原理

    ioc(反向控制) 原理:    在编码阶段,既没有实例化对象,也没有设置依赖关系,而把它交给Spring,由Spring在运行阶段实例化.组装对象.这种做法颠覆了传统的写代码实例化.组装对象.然后一 ...

  9. 数据库启动windows

    1.上 MongoDB官网下载数据库,下载之后选择自己想放的文件夹要记住文件夹位置,比如我下载之后就放在D盘,改文件夹为 mongodb 2.启动之前要给mongodb指定一个文件夹,这里取名为&qu ...

  10. cdnbest获取,删除,增加,修改域名列表,高级设置api示例

    <?php $uid = 28; $vhost = 'asdfw'; $token = getToken($uid, $vhost); print_r($token); //获取token fu ...