1. 主页 > 史诗殿堂 >

windows 异步IO操作的几种实现方法

文章目录

异步IO设备内核对象事件内核对象可警醒IO完成端口IO

异步IO

当我们读取一个文件时,一般情况下,线程是阻塞的,也就是说,当前线程在等待文件读取操作结束,这种方式叫同步IO。

Windows在系统底层为用户实现了另外一种高效的机制,叫重叠I/O,又称作异步I/O。

异步I/O提供了这样一种功能,当用户读取文件的时候,读取文件函数会立马返回结果不会阻塞线程,但是实际上文件并没有读取完,而是交给了系统底层自动去处理,这样文件的读取操作就不会阻塞住你的线程。

但是这种引发了一个问题,我们如何才能知道文件已经读取完毕了呢?

异步I/O注意事项: 一旦一个句柄是以异步I/O的方式打开的,那么: 1、句柄变为可等待的对象,就是说,它具有了有信号状态和无信号状态。 2、用overlapped结构体中的offset表示读取或者写入的位置。

typedef struct _OVERLAPPED {

ULONG_PTR Internal; //异步IO操作状态,是否成功。

ULONG_PTR InternalHigh; //操作了多少个字节。

union { //文件偏移。

struct {

DWORD Offset;

DWORD OffsetHigh;

} DUMMYSTRUCTNAME;

PVOID Pointer;

} DUMMYUNIONNAME;

HANDLE hEvent; //事件对象

} OVERLAPPED, * LPOVERLAPPED;

操作文件API函数:

CreateFile 创建/打开文件

ReadFile 读取文件

WriteFile 写入文件

WriteFileEX

ReadFile

OVERLAPPED 重叠I/O结构体

设备内核对象

Windows将设备句柄看作可同步的对象,即它可以处于有信号或处于无信号状态,当创建设备句柄、以异步的方式发送IO请求时,该句柄处于无信号状态,当异步IO完成之后,该句柄受信,通过WaitForSingleobject或WaitForMultipleObjects函数可以判断设备操作何时完成。

#include

#include

int main()

{

//打开一个文件

HANDLE file1 = CreateFile(

L"test.txt",

GENERIC_READ,

FILE_SHARE_READ,

NULL,

OPEN_EXISTING,

FILE_FLAG_OVERLAPPED,

NULL

);

//OVERLAPPED结构体专门用来存储异步IO的内容

OVERLAPPED overlapped{ 0 };

CHAR buff[0x100]{ 0 };

//读取的字节要设置为NULL

ReadFile(file1,

buff,

0x100,

NULL,

&overlapped

);

/*

........在此处可以添加其他代码..........

*/

//等待file1是否已经读取完,是否是有信号状态(表示线程已经结束,即读取完毕)

WaitForSingleObject(file1, -1);

DWORD CurSize = 0;

//读取是否成功

if (GetOverlappedResult(file1, &overlapped, &CurSize, TRUE))//False:不会等待,True:一直等待

{

printf("文件内容: %s\n", buff);

printf("实际读写数量: %d\n", CurSize);

}

CloseHandle(file1);

return 0;

}

该技术只能用于一个设备只发送一个IO请求,否则,若一个设备对应多个操作,当句柄受信时无法判断是该设备的那个操作完成。

事件内核对象

针对每个I/O操作绑定一个内核事件对象,并将等待事件等待函数等待该事件的受信,当I/O操作完成后系统使得与该操作绑定的事件受信,从而判断那个操作完成。

#include

#include

int main()

{

HANDLE File1 = CreateFileW(L"test.txt", GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

OVERLAPPED overlapped1{ 0 };

OVERLAPPED overlapped2{ 0 };

overlapped1.hEvent = CreateEventW(NULL, FALSE, FALSE, L"FileEvent1");

overlapped2.hEvent = CreateEventW(NULL, FALSE, FALSE, L"FileEvent2");

WCHAR buff1[100]{ 0 };

WCHAR buff2[100]{ 0 };

//注意: hEvent的事件作为读取文件时,我们无法获取读取的真正的字符数量,此处参数无意义NULL

ReadFile(File1, buff1, 100, NULL, &overlapped1);

ReadFile(File1, buff2, 100, NULL, &overlapped2);

/*

...

*/

//判断信号状态

WaitForSingleObject(overlapped1.hEvent/*判断事件*/, -1);

WaitForSingleObject(overlapped2.hEvent/*判断事件*/, -1);

printf("buff1:%s\n", buff1);

printf("buff2:%s\n", buff2);

return 0;

}

可警醒IO

当发出设备IO请求时,同时要求我们传递一个被称为完成例程的回调函数,当IO请求完成时调用该回调函数完成我们需要处理的工作。

#include

#include

VOID WINAPI lpCompletionRoutine(

DWORD dwErrorCode,

DWORD dwNumberOfBytesTransfered,

LPOVERLAPPED lpOverlapped

)

{

if (lpOverlapped->hEvent == (HANDLE)0x100)

{

printf("一\n");

}

else

{

printf("二\n");

}

}

int main1()

{

HANDLE File1 = CreateFileW(L"test.txt", GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

OVERLAPPED overlapped1{ 0 };

OVERLAPPED overlapped2{ 0 };

overlapped1.hEvent = (HANDLE)0x100;

overlapped2.hEvent = (HANDLE)0x101;

WCHAR buff1[100], buff2[100];

ReadFileEx(File1, buff1, 100, &overlapped1, lpCompletionRoutine);

ReadFileEx(File1, buff2, 100, &overlapped2, lpCompletionRoutine);

SleepEx(0, TRUE);

return 0;

}

完成端口IO

我不会。。。。。。。 以后在来填坑