/*
同步IO的缺点是, 在读写文件时, 如果文件太大, 或者读写的时间太长, 就会在读写函数中
阻塞住.
异步IO解决了这个问题, 异步IO读写文件时, 文件再大也不会阻塞住
但是异步IO要完成这样的特性是有一点付出的
异步读写文件后, 需要通过一些方式才能知道文件读写(IO任务)什么时候完成.
这里讲的是第二个方式, 通过等待事件对象的信号来处理已完成的IO任务
异步IO中, 无论是ReadFile还是WriteFile , 最后一个参数都需要传递一个OVERLAPPED
结构体变量的地址, 这个结构体中, 有一个hEvent的字段. 这个字段就是完成事件对象通知
的依赖.
IO任务完成以后, 系统会检查OVERLAPPED结构体中的hEveht字段是否为NULL,如果不为NULL
系统会调用SetEvent将hEveht设置为有信号.
因此, 我们想要利用这种方式, 就会在派发IO任务之前, 创建一个事件对象, 并把它赋值到
OVERLAPPED结构体中的hEveht字段中, 在派发IO任务之后, 创建一个线程,或者在某个位置
中调用WaitForMultipleObjects来等待事件对象的信号的就可以了.
*/
#include "stdafx.h"
#include <windows.h>
#include <process.h>
/*
* 继承OVERLAPPED
* 因为OVERLAPPED结构体中的字段并没有保存文件读写内容缓冲区的首地址
* 在原有的结构体基础之上想要扩展新的功能,在这里使用了C++的继承特性
* MyOverlapped前面的部分是OverLAPPED的字段
* 后面的部分是新添加的成员
**/
struct MyOVERLAPPED:public OVERLAPPED
{
public:
char * pBuff; // 用于保存缓冲区首地址
int nIndex; // 用于保存IO任务的序号
MyOVERLAPPED();
MyOVERLAPPED(int nIoSize,int nFileOffsetLow,int nFileOffsetHeight=0);
~MyOVERLAPPED();
};
// 默认构造函数
MyOVERLAPPED::MyOVERLAPPED() :pBuff(nullptr)
{
OVERLAPPED::hEvent = 0;
}
// 带参构造函数
MyOVERLAPPED::MyOVERLAPPED(int nIoSize, int nFileOffsetLow, int nFileOffsetHeight)
{
// 创建事件对象
OVERLAPPED::hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
// 保存文件读写偏移
OVERLAPPED::Offset = nFileOffsetLow;
OVERLAPPED::OffsetHigh = nFileOffsetHeight;
// 申请缓冲区保存文件内容
pBuff = new char[nIoSize];
}
// 析构函数,释放空间,释放事件对象
MyOVERLAPPED::~MyOVERLAPPED()
{
// 释放空间
if (pBuff != nullptr)
{
delete[] pBuff;
pBuff = nullptr;
}
// 关闭事件对象
if (hEvent != NULL)
{
CloseHandle(hEvent);
}
}
// 等待IO事件有信号的函数
unsigned int __stdcall IOProc(void *pArg)
{
MyOVERLAPPED * pMyOverlapped = (MyOVERLAPPED*)pArg;
WaitForSingleObject(pMyOverlapped->hEvent, INFINITE);
printf("[%d] IO任务完成,读取字节:%d,读取位置:%d,读取内容:[%s]\n}",
pMyOverlapped->nIndex,
pMyOverlapped->InternalHigh,
pMyOverlapped->Offset,
pMyOverlapped->pBuff);
// 释放空间
delete pMyOverlapped;
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hFile = CreateFile(
L"1.txt", // 文件名
GENERIC_READ, // 只读方式打开
FILE_SHARE_READ, // 共享读权限
NULL, // 安全描述符
OPEN_EXISTING, // 打开时文件必须存在
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, // 使用异步IO
NULL);
if (hFile == INVALID_HANDLE_VALUE) return 0;
int nSize = GetFileSize(hFile,NULL);
// 配置异步IO的信息
// 因为要传递到线程的函数中使用,所以,在这里必须要保存到堆空间中
// 如果是栈空间,则出了当前这个函数时,栈空间就会被释放
MyOVERLAPPED *ov = new MyOVERLAPPED;
ov->Offset = 0; // 要读取的文件偏移
ov->OffsetHigh = 0;
ov->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
ov->pBuff = new char[nSize]{};
ov->nIndex = 0; // 序号
// 派发IO任务
ReadFile(hFile, ov->pBuff,nSize,NULL,ov);
// 派发第二个IO任务 - 配置IO信息
// 下面代码的写法比上面的写法精简了很多,但是功能完全一样
int nFileRead = 50;
MyOVERLAPPED *ov2 = new MyOVERLAPPED(nFileRead,300);
ov2->nIndex = 1; // 序号
ReadFile(hFile, ov2->pBuff, nFileRead, NULL, ov2);
// 创建线程等到IO任务全部完成
HANDLE hThreadEvent[2] = {};
hThreadEvent[0] = (HANDLE)_beginthreadex(0, 0, IOProc, ov, 0, 0);
hThreadEvent[1] = (HANDLE)_beginthreadex(0, 0, IOProc, ov2, 0, 0);
WaitForMultipleObjects(_countof(hThreadEvent), hThreadEvent, TRUE, -1);
system("pause");
return 0;
}
【Windows原理】IO异步-等待事件对象
最新推荐文章于 2022-10-13 16:51:32 发布