// 同步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;
}
}
【Windows原理】异步IO-_APC(异步过程调用)
最新推荐文章于 2024-09-01 09:18:27 发布