【Windows原理】IO异步-等待事件对象

/*

同步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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值