一 什么是异步I/O

  同步I/O和异步I/O的关键不同就是在发出I/O请求后,线程是否会阻塞。当线程发出一个设备I/O请求的时候,线程会被挂起来,直到设备完成I/O请求为止,这称之为同步I/O。而对于异步I/O,当线程提交了一个设备I/O请求后,可以继续运行,当内核完成I/O的请求后会通知线程I/O已完成。由于与计算机执行的大多数其它操作相比,设备I/O是其中最慢的,所以使用异步I/O在大多数情况下可以大幅度提升程序的新能,当然一些个别情况就不一定使用啦。

二 异步I/O的分类

  在Windows下一共有四种异步I/O的技术,它们在启动I/O操作的方法以及用于操作何时完成的方法上有所不同:

  • 等待的 重叠I/O。线程发出I/O请求之后继续执行,当线程需要I/O请求的结果才能继续时,则要么等待文件句柄,要么等待在重叠结构中指定的一个事件。根据等待对象的不同,分为等待文件句柄的重叠I/O和等待事件的重叠I/O。等待文件句柄的重叠I/O没有多大用,因为它需要等待和此文件句柄关联的所有操作完成,同步I/O基本差不多。一般我们用的是等待事件的重叠I/O,使用人工事件来实现等待。
  • 完成例程的重叠I/O。完成例程,这个名词看上去似乎很高级,说白了就是个毁掉函数。这个方法允许我们向一个设备发出多个I/O请求,这些I/O请求中带有一个回调函数,即完成例程。当I/O请求完成时,如果线程处于可提醒状态,则系统会通知该线程调用它的异步队列中的回调函数来处理完成的I/O。
  • I/O完成端口。I/O完成端口允许我们向一个设备同时发出多个I/O请求。它允许一个线程发出I/O请求而另外一个线程对结果进行处理,这个是是和完成例程不同之处。这个技术具有很好的伸缩性和灵活性,算是异步I/O中最好的一种方式。I/O完成端口里面有一套线程池的调度策略,所以免去了我们去维护多线程的烦恼。

三  相关数据结构和函数

  下面我们来看看重叠I/O中的第一个方法----重叠I/O(等待)。先从了解重叠结构开始:

1  重叠结构 OVERLAPPED

  “overlapped”是执行I/O请求的时间与其他线程执行其他任务的时间是重叠的,OVERLAPPED的结构如下:

   

typedef struct _OVERLAPPED {
ULONG_PTR Internal; //系统保留参数,初始化时不用设置,返回值时保存已处理的I/O请求的错误码
ULONG_PTR InternalHigh; //系统保留参数,初始化时不用设置,返回值时保存已传输的字节数
union {
struct {
DWORD Offset; //文件偏移量的低位
DWORD OffsetHigh; //文件偏移量的高位
}; PVOID Pointer;
}; HANDLE hEvent; //事件句柄,必须是手动事件
} OVERLAPPED, *LPOVERLAPPED;

  参数说明:

  Offset 和OffsetHigh

  这两个成员构成一个64位的偏移量。如果当前操作的是文件设备,则表示当前文件I/O操作应该从哪里开始。当执行异步I/O时,系统会忽略与文件关联的内核文件指针,而是用OVERLAPPED中指定的起始偏移量,这样可以避免对同一个对象进行异步调用的时候出现混淆。对于非文件设备,则需要将这两个参数设置为0,否则I/O请求会失败,返回错误ERROR_INVALID_PARAMETER。

  hEvent

  不同的重叠I/O操作方法中,hEvent的设置不同。在可等待的重叠I/0操作中,如果等待的是操作的文件句柄,则不需要hEvent不需要设置;而如果通过OVERLAPPED结构中的hEvent来等待的,或者是I/O完成端口方法,则需要将此参数设置为一个人工事件,注意不能使自动事件。如果是I/O完成例程的方法,则hEvent一般都用来传递I/O操作的信息。

  Internal

  用来保存已经处理的I/O请求的错误码。当我们发出异步I/O请求后,设备驱动程序会将internal设置为STATUS_PENDING,表示没有错误,操作尚未开始。

  InternalHigh

  当异步I/O请求完成时,用来保存已经传输的字节数。

异步I/O的注意事项:

  1 在异步I/O请求完成之前,一定不恩能够移动或是销毁在发出I/O请求时所使用的数据缓存和OVERLAPPED机构,否则会使内存遭到破坏。因为我们传给I/O设备的是这两个数据的地址,而I/O设备并不知道我们已经销毁了它们。

  2 在执行异步I/O的时候,设备不必以先入先出的方式处理队列中的I/O请求。

  3 以异步I/O执行的时候,文件的读写等相关的操作返回为FALSE不一定表示失败。必须调用GetLastError,如果GetLastError返回的是ERRO_IO_PENDING,表示I/O请求已经成功的加入到I/O队列中,会在晚些时候完成。

2 获取重叠I/O状态

  当我们使用ReadFile或WriteFile向文件发送读写请求后,函数会立刻返回。大多数情况下,返回时I/O操作都未完成,所以返回的FALSE,自然我们也不知道传输的字节数了。所以我们需要另外一种方法来获取文件I/O完成时传输的字节数,这个函数便是GetOverlappedResult:

  

BOOL
WINAPI
GetOverlappedResult(
__in HANDLE hFile, //I/O文件句柄
__in LPOVERLAPPED lpOverlapped, //重叠结构
__out LPDWORD lpNumberOfBytesTransferred, //I/O完成时传输的字节数
__in BOOL bWait //Ture:一直等待,Flase:立刻返回
);

 参数hFile和lpOverlapped组合在一起可以唯一的表示一个I/O操作。当bWait设置为True,则函数会一直等待此I/O操作完成才会返回,此时lpNumberOfBytesTransfferred中即是传输的字节数。如果bWait设置为False,函数会立刻返回,如果返回会False且GetLastError()为ERRO_IO_PENDING,表示I/O尚未完成,需要轮询检查I/O是否完成。

3 取消队列中的重叠I/O请求

  如果想要取消一个已经加入到I/O队列但是尚未处理的I/O请求,则可以调用CancelIoEx取消。

BOOL
WINAPI
CancelIoEx(
__in HANDLE hFile,
__in_opt LPOVERLAPPED lpOverlapped
);

  CancelIoEx不仅可以取消掉本线程发出的相关文件的I/O请求,还可以将调用线程外的其他线程发出的待处理的I/O请求取消掉。这个函数会将hFile设备待处理的I/O请求中所有与lpOverlapped参数相关联的请求都取消掉。但是由于每个待处理的I/O请求都有一个其特定的OVERLAPPED结构,所以如果lpOverlpaped非空,则CancelIoEx每次只能取消一个请求。而lpOverlpaped为NULL的话,会取消掉hFileI/O请求队列中的所有I/O请求。

四 例子:使用可等待的重叠I/O进行文件复制操作

  这个示例程序使用事件来实现重叠I/O的等待。这个程序实现从输入文件中异步读数据,然后异步的写数据到输出文件中。程序采用多缓冲区的方法进行文件的转换,假设输入和输出各采用N个缓冲区,则N个输入缓冲和N个输出缓冲需要N个输入重叠结构和N个输出重叠结构与其对应。初始时,N个输入缓冲分别发出重叠的读操作,然后程序使用WaitForMultipleObjects来等待单一的I/O操作完成事件,表示一个读或写操作完成。当一个读操作完成时,则对缓冲区进行复制,然后发起一个写操作请求。当写完成时,就可以进行下一个读操作请求了。

最新文章

  1. 【javascript杂谈】你所不知道的replace函数
  2. 手动安装python后,交互模式下退格键乱码
  3. October 10th 2016 Week 42nd Monday
  4. websocket和swoole
  5. (转)A Beginner's Guide To Understanding Convolutional Neural Networks
  6. poj 3615(floyd变形)
  7. HDU 5437 Alisha’s Party (优先队列模拟)
  8. 《如何让TT T4模板输出多个文件(VS2010中)》-- access911.net 文章
  9. Android 如何修改自动同步数据的默认开关 M
  10. hdu 1150 Machine Schedule (经典二分匹配)
  11. centos安装docker容器
  12. ⑾bootstrap组件 徽章 大屏 页头 基础案例
  13. AtCoder Regular Contest 071
  14. 【html5】html5离线存储
  15. 在ASP.NET MVC 项目中 使用 echarts 画统计图
  16. 20165309 《网络对抗技术》实验五:MSF基础应用
  17. 【新知识】队列&bfs【洛谷p1996约瑟夫问题&洛谷p1451求细胞数量】
  18. Oracle 故障整理
  19. 黑帆第一季/全集Black Sails迅雷下载
  20. 360电影主页和详情页爬去入Mysql库链表读取--lowbiprogrammer

热门文章

  1. C# random(number)
  2. 移植FreeModbus+ModbusMaster+STM32至RT-Thread(初步)
  3. 多线程/进度条应用(progressbar)
  4. flex-mp3
  5. 【M1】仔细区别pointers和references
  6. mysql备份工具 :mysqldump mydumper Xtrabackup 原理
  7. C++11 类内初始化
  8. JavaScript 中 typeof 知多少?
  9. HelloSpark.scala
  10. oracle SELECT INTO 和 INSERT INTO SELECT 两种表复制语句详解