IOCP异步优化
一、内存操作和IO操作
在计算机运行执行程序的世界里,从如何得到处理结果分成两大类:
1. 内存操作: CPU在内存里面完成计算,然后得到处理结果。
2. IO操作: CPU会把内存中的程序委托给其他的网络、磁盘等驱动程序,让这些外部的驱动程序来进行具体的处理,处理完成以后再返回给内存程序。对于这两类操作的优化方式是不一样的。内存操作的特点是占用CPU资源,CPU不断的计算。对于内存密集型的操作(Compute-Bound Operation)的优化,我们可以把一个大任务拆分成多个互不影响的子任务,那么就能让多个CPU同时参与运算,最后合并子任务的结果,所花的时间自然就少了。所以内存密集型的操作(Compute-Bound Operation)的优化有一个前提:超线程、多核、甚至是真正的多个CPU的计算机能够同时运行多个线程,对于只有一个CPU的计算机不适合。多线程之间的状态切换是需要额外的CPU资源的。IO操作的特点是基本不占用CPU资源,但是它会占用当前的工作者线程,并使其进入等待状态,等待IO完成的处理结果,然后在继续执行。但是在ASP.NET这种天然多线程的环境里,CLR线程池容量是有上限的,这个上限也代表了应用程序最多可以同时执行的请求数量。如果我们CLR线程池的所有线程都进入了IO等待状态,当再有新用户进来,我们的服务就停止响应了。目前我们IO操作的缺点是当前工作者线程同步等待IO,任何IO处理都会霸占一条工作者线程。所以对于IO密集型的操作(IO-Bound Operation)的优化,我们的思路是使用IOCP(I/O Completion Port)。IOCP翻译了中文是IO完成端口,它是一种异步形态,原理是这样的:当前工作者线程在进行IO处理时,委托给某个设备驱动程序,然后自己返回线程池,当IO完成后,OS会通过IOCP提醒CLR它工作已经完成,当CLR接收到通知后,会唤醒一个I/O线程并且运行用户的回调。
I/O线程:是CLR线程池中预先保留出来的部分线程,这部分线程的作用是为了分发从IOCP中的回调。I/O线程由CLR调用。所以通常情况下,开发者并不会直接用到它。工作者线程和I/O线程区别:它们都是普通的线程,但是CLR线程池中区分它们的目的是为了避免线程都去处理I/O回调而被耗尽,从而引发死锁。关于CLR线程池的细节可以看:线程池的作用和CLR线程池
二、IOCP异步优化
ASP.NET天生就是多线程的运行环境,所以内存密集型的操作(Compute-Bound Operation),我们推荐单线程运算为原则。如果我们在具体业务逻辑里运用了多线程,也意味着系统将对多线程之间的状态切换产生额外的开销。从而加重了服务器的负担。
在IO密集型的操作(IO-Bound Operation)中,我们推荐使用IOCP模式。当执行I/O操作的时候,无论是同步I/O操作还是异步I/O操作,都会调用的Windows的API方法,比如,当读取文件的时候,调用ReadFile函数。该方法会将你的当前线程从用户态转变成内核态,会生成一个I/O请求包,并且初始化这个请求包,这个包中包含一个文件句柄,一个偏移量和一个Byte[]数组。ReadFile会向内核传递,根据这个请求包,windows内核知道需要将这个I/O操作发送给哪个硬件设备。这些I/O操作会进入设备自己的处理队列中,该队列由这个设备的驱动程序维护。
如果此时是同步I/O操作,那么在硬件设备操作I/O的时候,发出I/O请求的线程由于无事可做被windows变成睡眠状态,当硬件设备完成操作后,再唤醒这个线程。这种方式非常直接,但是性能不高,如果请求数很多,那么休眠的线程数也很多,浪费了大量资源。
如果是异步I/O操作,.Net中异步的I/O操作为BeginXXX的形式。该方法在Windows把I/O请求包发送到设备的处理队列后就返回了。同时,在调用异步I/O操作的时候,即调用BeginXXX方法的时候,需要传入一个委托,该委托方法会随着I/O请求包一路传递到设备的驱动程序。在设备处理完I/O请求包后,将该委托再放到CLR线程池中的I/O线程队列里。之前说到过,在CLR内部维护了一个IOCP(I/O completion port),它提供了处理多个异步I/O请求的线程模型,可以把这个IOCP看做是一个消息队列,当一个进程创建了一个IOCP,即创建了一个队列。当异步I/O请 求完成时,设备驱动程序就会生成一个I/O完成包,将它按照FIFO方式排队列入该完成端口。之后,会由I/O线程提取完成I/O请求包,并调用之前的委托。注意:异步调用服务时,回调函数都是运行于CLR线程池的I/O线程当中。
具体的在.NET的代码实例:
static void Main(string[] args)
{
WebRequest request = HttpWebRequest.Create("http://www.baidu.com");
request.BeginGetResponse(HandleAsyncCallback, request);
}
static void HandleAsyncCallback(IAsyncResult ar)
{
WebRequest request = (WebRequest)ar.AsyncState;
WebResponse response = request.EndGetResponse(ar);
// more operations...
}
IOCP中有2个队列,一个是先进先出的队列,存放的是IO完成包,即已经完成的IO操作需要执行回调方法,因此先进先出的方式是非常公平的。
还有一个队列是线程队列,IOCP会预分配一些线程在这个队列中,这样会比即时创建线程处理I/O请求速度更快。这个队列是后进先出的,好处是下一个请求的到来可能还是用之前的线程来处理,就不需要进行线程上下文切换,提高了性能。
在《Pro .NET Performance》中有如下一个示意图:
- Druid-目前最好的连接池
- 【算法】哈希表的诞生
- java中获得文件大小代码
- 【Java】泛型学习笔记
- 读写文件具体操作
- 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)
- ERROR 1396 (HY000): Operation CREATE USER faile...
- hive安装后测试
- 【算法】论平衡二叉树(AVL)的正确种植方法
- 【JavaScript】 JS面向对象的模式与实践
- log4j:ERROR Failed to load driver
- 前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例
- 前端MVC Vue2学习总结(六)——axios与跨域HTTP请求、Lodash工具库
- spring之config.xml完整版示例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- MySQL 案例:Update 死锁详解
- Android 线程之自定义带消息循环Looper的实例
- 详解Androidstudio3.0 关于Gradle报错的问题(小结)
- Android开发实现文件关联方法介绍
- Android开发获取重力加速度和磁场强度的方法
- Android自定义View圆形和拖动圆跟随手指拖动
- 简单好用的Adapter—ArrayAdapter详解
- Android开发之图片切割工具类定义与用法示例
- Android开发之超强图片工具类BitmapUtil完整实例
- Android使用URL读取网络资源的方法
- Android控件RecyclerView实现混排效果仿网易云音乐
- MUI整合上拉下拉的写法
- 详解OkSocket与Android的简单使用
- 浅谈Android Studio 3.0 的一些小变化
- 详解如何使用Android Studio开发Gradle插件