同步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(等待)。先从了解重叠结构开始:
“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队列中,会在晚些时候完成。
当我们使用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是否完成。
如果想要取消一个已经加入到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的等待。这个程序实现从输入文件中异步读数据,然后异步的写数据到输出文件中。程序采用多缓冲区的方法进行文件的转换,假设输入和输出各采用N个缓冲区,则N个输入缓冲和N个输出缓冲需要N个输入重叠结构和N个输出重叠结构与其对应。初始时,N个输入缓冲分别发出重叠的读操作,然后程序使用WaitForMultipleObjects来等待单一的I/O操作完成事件,表示一个读或写操作完成。当一个读操作完成时,则对缓冲区进行复制,然后发起一个写操作请求。当写完成时,就可以进行下一个读操作请求了。
重叠I/O之可等待的重叠I/O【系列一】,布布扣,bubuko.com
原文:http://www.cnblogs.com/1314NNNKKK/p/3709292.html