Nodejs之路:异步I/O的过程
对于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的过程的更多相关文章
- NodeJS中的异步I/O、事件驱动
nodejs的主要特点是单线程.异步I/O.事件驱动.让我们先大概了解一下这些名词的意思. 单线程 单线程是任务按照顺序执行的,并且每次只执行一个任务,只有前面的任务执行完成以后,后面的任务才执行.在 ...
- AJAX异步请求原理和过程
AJAX 指异步 JavaScript 及 XML(Asynchronous JavaScript And XML),它不是一种新的编程语言,而是一种使用现有标准的新方法. AJAX 基于 JavaS ...
- nodejs之路-[0]安装及简易配置
题外话: 之前写过ubuntu下编译nodejs- 传送门:Ubuntu15.04编译安装nodejsV0.12.3 只是如今基本在win下做开发了-. 就以这篇帖子为开头,作为我踏上nodejs之路 ...
- nodejs 中的异步之殇
nodejs 中的异步之殇 终于再次回到 nodejs 异步中,以前我以为异步在我写的文章中,已经写完了,现在才发现,还是有很多的地方没有想清楚,下面来一一说明. 模块同步与连接异步 大家应该,经常使 ...
- Nodejs之路:非I/O的异步API
本篇主要介绍setTimeout,setInterval,setImmediate和process.nextTick. 1,定时器 Node中的定时器和浏览器中用法一致.区别在于:在Node中,执行到 ...
- NodeJS学习之异步编程
NodeJS -- 异步编程 NodeJS最大的卖点--事件机制和异步IO,对开发者并不透明 代码设计模式 异步编程有很多特有的代码设计模式,为了实现同样的功能,使用同步方式和异步方式编写代码会有很大 ...
- 深入理解nodejs中的异步编程
目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...
- Nodejs爬虫进阶=>异步并发控制
之前写了个现在看来很不完美的小爬虫,很多地方没有处理好,比如说在知乎点开一个问题的时候,它的所有回答并不是全部加载好了的,当你拉到回答的尾部时,点击加载更多,回答才会再加载一部分,所以说如果直接发送一 ...
- 如何优雅的处理Nodejs中的异步回调
前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O ...
随机推荐
- PP.io的三个阶段,“强中心”——“弱中心”——“去中心”
什么是PP.io? PP.io是我和Bill发起的存储项目,目的在于为开发者提供一个去中心化的存储和分发平台,能做到更便宜,更高速,更隐私. 当然做去中心化存储的项目也有好几个,FileCoin,Si ...
- centos7 安装gitlab任意版本
主要还是根据官网:https://www.gitlab.cc/installation/#centos-7 1.安装依赖: sudo yum install curl policycoreutils ...
- python初接触
浑浑噩噩的过了3年多大学生涯,就借此开始下定决心好好的学好一门技术,为心中那个游戏梦而努力. 正式开课的第一天(也算不的吧,毕竟昨天老师也和我们讲了python课程的大纲内容).自我总结下今天的内容吧 ...
- [mysql,2018-02-28] bat安装、启动mysql,并创建数据库、表
@echo off f: cd F:\mysql-win32 @echo off&setlocal enabledelayedexpansion cd bin echo ###### 停止当前 ...
- Java并发编程75个问答
1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...
- python grib气象数据可视化
基于Python的Grib数据可视化 利用Python语言实现Grib数据可视化主要依靠三个库——pygrib.numpy和matplotlib.pygrib是欧洲中期天气预报中心 ...
- 为何要使用ViewModel
ViewModel类是用来存储和管理与UI相关的数据,在设计之初就考虑到生命周期的影响.ViewModel允许数据在屏幕旋转等配置变化后存活. Android framework管理UI控制器(如Ac ...
- python大法好——网络编程
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
- 使用AsyncTask类实现简单的异步处理操作
AsyncTask: 1.这是一种相比Handler更轻量级的处理异步任务的工具类 2.它和Handler类一样,都是为了不影响主线程(UI)而使用的((注:UI的更新只能在主线程中完成) 3.这个工 ...
- CentOS6.5搭建OpenVas完全搭建手册(搭建过程总结及小记)
一.OpenVAS 介绍 1.关于OpenVAS OpenVAS(Open Vulnerability Assessment System)是一套开源的漏洞扫描系统,早期Nessus 是其中一个最流行 ...