针对于
WaitForMultipleObjectsEx
最后一个参数的用途,有
如下,异步文件操作实例:
利用ReadFileEx/WriteFileEx,这对函数使用回调函数来进行读写完成的通知。
BOOL ReadFileEx(
HANDLE hFile, // handle to file
LPVOID lpBuffer, // data buffer
DWORD nNumberOfBytesToRead, // number of bytes to read
LPOVERLAPPED lpOverlapped, // offset
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // completion routine
);
hFile为文件句柄。
lpBuffer指明了写入数据的内存区指针。
nNumberOfBytesToRead为要求读入的数据字节数。
lpOverlapped为一个OVERLAPPED的结构,这个结构hEvent字段将被系统忽略,但是通过Offset和OffsetHigh字段来表明开始读文件的位置。
lpCompletionRoutine为一个通知用的回调函数。
函数的最后一个参数指明了一个回调函数,这个回调函数称为一个告警函数。函数必须具有这样的原型:
VOID CALLBACK FileIOCompletionRoutine(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped // I/O information buffer
);
dwErrorCode为错误代码,如果为0表示正确,为ERROR_HANDLE_EOF表示到达文件的末尾。
dwNumberOfBytesTransfered为成功传送的字节数,如果发生错误,这个值为0。
lpOverlapped为一个OVERLAPPED的结构,这个结构必须与调用ReadFileEx时指向相同的数据区,并且在调用ReadFileEx后不能手工更改这个结构中的字段。
那么如何检测回调函数已经被调用了(文件操作已经完成),你可以设置一个全局的同步量来进行通知,但是系统提供了其他简便的方法供开发者使用,这就是SleepEx WaitForMultipleObjectsEx 和WaitForSingleObjectEx。
当线程调用ReadFileEx或WriteFileEx时,提供了一个告警函数,这个告警函数会被放入一个队列,当操作完成时操作系统会从队列中取出这些函数进行调用。所以对于属于本进程内部所产生的需要等待被调用的告警函数来说可以直接使用SleepEx对当前线程进行休眠并等待当前提交所有的告警函数被执行。如果希望等待指定文件操作上的告警函数被调用你可以使用WaitForMultipleObjectsEx或WaitForSingleObjectEx。这三个函数的原型为:
DWORD SleepEx(
DWORD dwMilliseconds, // time-out interval
BOOL bAlertable // early completion option
);
DWORD WaitForSingleObjectEx(
HANDLE hHandle, // handle to object
DWORD dwMilliseconds, // time-out interval
BOOL bAlertable // alertable option
);
DWORD WaitForMultipleObjectsEx(
DWORD nCount, // number of handles in array
CONST HANDLE *lpHandles, // object-handle array
BOOL fWaitAll, // wait option
DWORD dwMilliseconds, // time-out interval
BOOL bAlertable // alertable option
);
这三个函数和Sleep WaitForSingleObject WaitForMultipleObjects的差别就在于多了最后一个参数bAlertable,这个参数需要设置为TRUE表明等待文件异步操作的完成。通过检测函数返回值可以得知文件操作是否完成,例如下面的代码:
ReadFileEx(hFile,pbRead,1024*1024*50,&overlap,MyIORoutine);
while(WAIT_IO_COMPLETION != SleepEx(1,TRUE) )//检测文件操作是否完成
//while (WaitForSingleObjectEx(hFile,1,TRUE) != WAIT_OBJECT_0 )
//在这里WaitForSingleObjectEx和SleepEx具有相同作用
{
DoSomething();
}
对于SleepEx来说如果返回WAIT_IO_COMPLETION则表示异步操作完成,而对于文件对象来说如果异步操作完成文件对象就会变为有信号状态。下面的例子是一个利用告警回调函数实现的文件异步读写。
VOID CALLBACK MyIORoutine(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped // I/O information buffer
)
{//定义一个简单的回调函数
printf("文件读完成\n");
}
void DoSomething(void)
{
printf("current time %d\n",GetTickCount());
Sleep(2000);//假设耗时的操作需要两秒钟
}
//下面是使用异步读的示范代码,假设c:\temp\large_file.dat文件有130MB大小()
//一次性读入50MB字节,在读入的过程中进行一些其他操作
void ReadM(void)
{
HANDLE hFile = CreateFile("c:\\temp\\large_file.dat",GENERIC_READ,0,
NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL,NULL);
if( INVALID_HANDLE_VALUE != hFile )
{
BYTE *pbRead = new BYTE[1024*1024*50];//50MB字节
OVERLAPPED overlap;
overlap.Offset = 0;
overlap.OffsetHigh =0;
overlap.hEvent = NULL; //使用告警函数时无需要使用事件
DWORD dwBegin= GetTickCount();//记录开始时间
printf("begin time %d\n",dwBegin);
ReadFileEx(hFile,pbRead,1024*1024*50,&overlap,MyIORoutine);
while(WAIT_IO_COMPLETION != SleepEx(1,TRUE) )//检测文件操作是否完成
//while (WaitForSingleObjectEx(hFile,1,TRUE) != WAIT_OBJECT_0 )
//在这里WaitForSingleObjectEx和SleepEx具有相同作用
{
DoSomething();//在文件读的执行过程中进行其他操作
}
printf("耗时%d\n",GetTickCount()-dwBegin);
//操作完成
CloseHandle(hFile);
delete pbRead;
}
}
WriteFileEx的用法与ReadFileEx的用法是类似的。
如下结论:
在磁盘操作中磁盘写比读需要花更多的时间,并且大文件的异步写可以更加有效的提高CPU利用率。但是异步操作会给开发和调试带来一些麻烦,所以我建议除非在非常必要(性能要求非常高,文件非常大)的情况下才使用异步的磁盘读写。
祥见:http://huihua.hebtu.edu.cn/hhcmc/study/teach_vc/teach_sp_52.htm
异步文件操作 代码示例
#include "stdafx.h"
VOID CALLBACK MyIORoutine(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped // I/O information buffer
)
{
printf("callback MyIORoutine start....\n");
SleepEx(5000,false);
}
void DoSomething(void)
{
printf("current time %d\n",GetTickCount());
Sleep(4000);
}
void ReadM(void)
{
HANDLE hFile = CreateFile("E:\\large_file.dat",GENERIC_READ,0,
NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED|FILE_ATTRIBUTE_NORMAL,NULL);
if( INVALID_HANDLE_VALUE != hFile )
{
BYTE *pbRead = new BYTE[1024*1024*50];//50M
OVERLAPPED overlap;
overlap.Offset = 0;
overlap.OffsetHigh =0;
overlap.hEvent = NULL;
DWORD dwBegin= GetTickCount();//
printf("begin time %d\n",dwBegin);
ReadFileEx(hFile,pbRead,1024*1024*50,&overlap,MyIORoutine);
DWORD msg = WaitForSingleObjectEx(hFile,1,TRUE);
if(msg == WAIT_OBJECT_0)
//while (WaitForSingleObjectEx(hFile,1,TRUE) == WAIT_OBJECT_0 )
{
DoSomething();
}else printf("hello world\n");
printf("end %d\n",GetTickCount()-dwBegin);
CloseHandle(hFile);
delete pbRead;
}
}
int main(int argc, char* argv[])
{
ReadM();
return 0;
}