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
我不会。。。。。。。 以后在来填坑
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209678.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律