windows 异步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 <Windows.h>
#include <iostream>
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 <Windows.h>
#include <iostream>
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 <Windows.h>
#include <iostream>
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

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

posted @   hugeYlh  阅读(335)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示