【Windows原理】异步IO-_APC(异步过程调用)

// 同步IO的缺点是, 在读写文件时, 如果文件太大, 或者读写的时间太长, 就会在读写函数中
// 阻塞住.  
// 异步IO解决了这个问题, 异步IO读写文件时, 文件再大也不会阻塞住
// 但是异步IO要完成这样的特性是有一点付出的
// 异步读写文件后, 需要通过一些方式才能知道文件读写(IO任务)什么时候完成. 
// 这里讲的是第三个方式, 通过设置IO完成函数来处理已完成的IO任务.
// 在Windows 中, ReadFile和WriteFile还有另外一个版本的函数, 那就是ReadFileEx 和
// WriteFileEx, 这两个增强版的函数中, 都是添加了一个参数, 这个参数是一个函数指针.
// 当I/O任务完成之后, 系统内部会在一段时间内调用这个函数指针. 但是系统并不是无条件调
// 用该函数指针的. 
// 每个线程都有一个APC队列(异步过程调用队列), 这个队列实际上就是一个数组, 这个数组中
// 的每一个元素都是一个函数的地址. 线程被挂起时, 这些函数会被系统调用, 系统会从APC队
// 列中移除已经被调用过的函数.
// 线程挂起时, 默认是不执行APC队列中的函数的, 想要在线程挂起时让系统自动调用APC队列中
// 的函数, 必须使用以下的函数来挂起线程:
//   1. SleepEx
//   2. WaitForSignalObjectEx
//   3. WaitForMultipleObjectEx
//   4. SignalObjectAndWait
//   5. GetQueuedCompletionStatusEx
//   6. MsgWaitMultipelObjectEx
// 这些函数中,有一个参数 BOOL bAlertble 这个参数就用于指定是否调用APC队列中的函数
// 的.异步I/O中的可提醒I/O通知方式实际的原理是这样的:当I/O任务完成后,系统会将完成函
// 数的地址发送到线程APC队列( 通过QueueUserAPC 发送 ),然后当线程被指定的函数挂起 
// 后 ,可提醒I/O的完成函数才会被调用.

#include "stdafx.h"
#include <windows.h>
#include <process.h>
struct MyOVERLAPPED:public OVERLAPPED
{
public:
    char * pBuff; // 用于保存缓冲区首地址
    int nIndex;    // 用于保存IO任务的序号
    MyOVERLAPPED();
    MyOVERLAPPED(int nSize, int nFileOffsetLow, int nFileOffsetHeight = 0);
    ~MyOVERLAPPED();
};

// 完成函数
VOID WINAPI OverlappedCompliteFun(
    DWORD dwErrorCode,              // 错误码
    DWORD dwNumberOfBytesTransfered, // 成功读写的字节数
    LPOVERLAPPED lpOverlapped        // 重叠IO结构体指针
    )
{
    MyOVERLAPPED* pMyOverlapped = (MyOVERLAPPED*)lpOverlapped;
    printf("[%d]IO任务完成,读取字节:%d,读取位置:%d,读取内容:[%s]\n",pMyOverlapped->nIndex,pMyOverlapped->Internal,pMyOverlapped->Offset,pMyOverlapped->pBuff);

    // 释放空间
    delete pMyOverlapped;
}

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,NULL);

    if (hFile == INVALID_HANDLE_VALUE)return 0;

    int nFileRead = 50;
    int nReadPos = 0;

    // 第一个IO任务
    MyOVERLAPPED * vo = new MyOVERLAPPED(nFileRead,nReadPos);
    vo->nIndex = 0;
    ReadFileEx(hFile, vo->pBuff, nFileRead, vo, OverlappedCompliteFun);

    // 第二个IO任务
    nFileRead = 80;
    nReadPos = 500;
    MyOVERLAPPED * vo2 = new MyOVERLAPPED(nFileRead,nReadPos);
    vo2->nIndex = 1;  // 任务序号
    ReadFileEx(hFile, vo2->pBuff, nFileRead, vo2, OverlappedCompliteFun);

    // 如果将下面这行代码注释掉,完成函数将不会被调用
    SleepEx(3, TRUE);
    system("pause");

    return 0;
}

// 默认构造函数
MyOVERLAPPED::MyOVERLAPPED() :pBuff(nullptr)
{
    OVERLAPPED::hEvent = 0;
}

// 初始化构造函数
MyOVERLAPPED::MyOVERLAPPED(int nIoSIze, int nFileOffsetLow, int nFileOffsetHeight)
{
    // 创建事件对象
    OVERLAPPED::hEvent = 0; // 不需要事件对象

    // 保存文件读写偏移
    OVERLAPPED::Offset = nFileOffsetLow;
    OVERLAPPED::OffsetHigh = nFileOffsetHeight;

    // 申请缓冲区保存文件内容
    pBuff = new char[nIoSIze]{};
}

// 析构函数,释放空间,事件对象
MyOVERLAPPED::~MyOVERLAPPED()
{
    // 释放空间
    if (pBuff != nullptr)
    {
        delete[] pBuff;
        pBuff = nullptr;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值