Windows下异步IO的实现方式
Windows下有四种异步IO(Overlapped IO)的实现方式:
- 通过等待文件句柄的激发来实现;
- 通过
OVERLAPPED
结构体中的手动重置事件hEvent
的激发来实现; - 通过异步过程调用(Asynchronous Procedure Call,APC)来实现;
- 通过IO完成端口(IO Completion Ports,IOCP)来实现。
由于IO操作非常的耗时,如果应用程序在IO操作过程中被阻塞了,那么会严重影响程序的性能。有些情况下,一个线程中遇到IO操作时,并不需要对IO操作进行等待,此时,可以让IO操作异步执行,线程继续做其他事情,最后再对IO的操作结果进行响应,这样就大大提升程序的性能。采用异步IO,可以最大化的利用多核的特性来处理IO请求,提高应用程序的并行处理能力。
文件相关操作
一般情况下,IO操作后,需要对IO结果给予响应处理。在Windows下,一个打开的文件可以跟一个句柄关联:文件句柄
,而文件句柄是一个核心对象,可以通过检测句柄是否被激发来确定IO操作是否完成。
Windows下的文件操作主要是三个函数,都是通过文件句柄来操作文件:
- CreateFile()
- ReadFile()
- WriteFile()
关闭文件只需要CloseHandle()来关闭文件句柄即可(注意,Windows下任何被打开的句柄理论上都应该要主动Close,不然会造成资源泄露)。
CreateFile()函数
HANDLE CreateFile(
LPCSTR lpFileName, 指向文件名称
DWORD dwDesiredAccess, 存取模式(读或写)
DWORD dwShareMode, 共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, 安全属性
DWORD dwCreationDisposition, 如何产生
DWORD dwFlagsAndAttributes, 文件属性
HANDLE hTemplateFile 一个临时文件
);
各个参数的详细介绍请看此函数的声明处的解释。
如果想要使用异步IO,则形参dwFlagsAndAttributes
是关键。如果此值传入了枚举FILE_FLAG_OVERLAPPED
,则此文件就可以用异步来操作。因此,使用异步IO,必须要传入此枚举值。
有一点需要注意的是,异步IO中对同一个文件可以同时读或者写文件的不同位置,因此没有所谓的当前文件位置,每次读写操作都要包含文件位置。
ReadFile()函数
BOOL ReadFile(
HANDLE hFile, 要读取的文件
LPVOID lpBuffer, 接收缓冲区
DWORD nNumberOfBytesToRead, 将要读取的字节数
LPDWORD lpNumberOfBytesRead, 实际读取的字节个数的地址
LPOVERLAPPED lpOverlapped 指向overlapped结构体
);
WriteFile()
形参与ReadFile()一致。
如果文件是以异步方式打开,则读写函数的最后一个形参必须指向OVERLAPPED
结构体:
typedef struct _OVERLAPPED {
ULONG_PTR Internal;
ULONG_PTR InternalHigh;
union {
struct {
DWORD Offset;
DWORD OffsetHigh;
} DUMMYSTRUCTNAME;
PVOID Pointer;
} DUMMYUNIONNAME;
HANDLE hEvent;
} OVERLAPPED, *LPOVERLAPPED;
结构体中有两点需要注意,DUMMYSTRUCTNAME
和hEvent
。其中,DUMMYSTRUCTNAME
是确定读写文件的位置,hEvent
是一个手动重置事件,当文件操作完成后,此事件被激发。
需要注意的是,OVERLAPPED
不能存储在局部变量里,应该存储为全局变量或者存储在堆中。