磁盘文件的正常读写与异步读写 (3)

在上面的例子中没有过多的进行错误检查,如果大家有兴趣可以自己运行一下这个例子,看看异步文件读写对性能的影响。在我自己的计算机PII 450 IBM 30GB硬盘上同步读比异步读慢了大约10%左右,这主要时因为数据处理时间我设置为两秒钟,如果设置得足够长,会显示出异步和同步处理时的差异极限。此外由于磁盘缓存得作用也会影响结果,所以如果读入的数据量更大将会产生更明显的差异,这是因为虽然异步读写会在调用等待函数上也会耗费一些时间,所以如果数据量小就无法分辨出差异。

请记住OVERLAPPED参数在文件操作执行完以前不要手工修改结构内的值,因为系统会使用结构中的数据。

对于WriteFile操作也可以用相同方法,在WriteFile中磁盘操作将耗费更多的时间,所以使用异步写更能体现优势,你可以将这个例子改为磁盘写后自己测试一下。

下载利用ReadFile进行异步文件读的示范代码

第二种方法,利用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的用法是类似的。

下载利用ReadFileEx进行异步文件读的示范代码

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzxpdf.html